Skip to content

Commit

Permalink
Merge pull request #987 from tahoe-lafs/3617.web-python-3-part-4
Browse files Browse the repository at this point in the history
Port allmydata.web to Python 3, part 4.

Fixes ticket:3617
  • Loading branch information
itamarst committed Mar 2, 2021
2 parents 99f6a69 + 41590ff commit e1ef9d4
Show file tree
Hide file tree
Showing 8 changed files with 90 additions and 48 deletions.
2 changes: 1 addition & 1 deletion misc/operations_helpers/provisioning/run.py
Expand Up @@ -30,7 +30,7 @@ class Root(rend.Page):

def run(portnum):
root = Root()
root.putChild("tahoe.css", static.File("tahoe.css"))
root.putChild(b"tahoe.css", static.File("tahoe.css"))
site = appserver.NevowSite(root)
s = strports.service("tcp:%d" % portnum, site)
s.startService()
Expand Down
Empty file added newsfragments/3617.minor
Empty file.
6 changes: 5 additions & 1 deletion src/allmydata/util/_python3.py
Expand Up @@ -117,12 +117,16 @@
"allmydata.util.spans",
"allmydata.util.statistics",
"allmydata.util.time_format",
"allmydata.web.common",
"allmydata.web.check_results",
"allmydata.web.common",
"allmydata.web.directory",
"allmydata.web.filenode",
"allmydata.web.info",
"allmydata.web.introweb",
"allmydata.web.logs",
"allmydata.web.operations",
"allmydata.web.private",
"allmydata.web.root",
"allmydata.webish",
]

Expand Down
9 changes: 7 additions & 2 deletions src/allmydata/util/netstring.py
Expand Up @@ -14,14 +14,19 @@

from past.builtins import long

try:
from typing import Optional, Tuple, List # noqa: F401
except ImportError:
pass

def netstring(s):

def netstring(s): # type: (bytes) -> bytes
assert isinstance(s, bytes), s # no unicode here
return b"%d:%s," % (len(s), s,)

def split_netstring(data, numstrings,
position=0,
required_trailer=None):
required_trailer=None): # type (bytes, init, int, Optional[bytes]) -> Tuple[List[bytes], int]
"""like string.split(), but extracts netstrings. Ignore all bytes of data
before the 'position' byte. Return a tuple of (list of elements (numstrings
in length), new position index). The new position index points to the first
Expand Down
70 changes: 40 additions & 30 deletions src/allmydata/web/directory.py
@@ -1,12 +1,16 @@
"""
TODO: When porting to Python 3, the filename handling logic seems wrong. On
Python 3 filename will _already_ be correctly decoded. So only decode if it's
bytes.
Also there's a lot of code duplication I think.
Ported to Python 3.
"""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals

from past.builtins import unicode
from future.utils import PY2
if PY2:
from future.builtins import filter, map, zip, ascii, chr, hex, input, next, oct, open, pow, round, super, bytes, dict, list, object, range, max, min # noqa: F401
# Don't use Future's str so that we don't get leaks into bad byte formatting
from past.builtins import unicode as str

from urllib.parse import quote as url_quote
from datetime import timedelta
Expand Down Expand Up @@ -143,7 +147,7 @@ def _got_child(self, node_or_failure, req, name):
terminal = (req.prepath + req.postpath)[-1].decode('utf8') == name
nonterminal = not terminal #len(req.postpath) > 0

t = unicode(get_arg(req, b"t", b"").strip(), "ascii")
t = str(get_arg(req, b"t", b"").strip(), "ascii")
if isinstance(node_or_failure, Failure):
f = node_or_failure
f.trap(NoSuchChildError)
Expand Down Expand Up @@ -225,7 +229,7 @@ def render_DELETE(self, req):
@render_exception
def render_GET(self, req):
# This is where all of the directory-related ?t=* code goes.
t = unicode(get_arg(req, b"t", b"").strip(), "ascii")
t = str(get_arg(req, b"t", b"").strip(), "ascii")

# t=info contains variable ophandles, t=rename-form contains the name
# of the child being renamed. Neither is allowed an ETag.
Expand Down Expand Up @@ -263,7 +267,7 @@ def render_GET(self, req):

@render_exception
def render_PUT(self, req):
t = unicode(get_arg(req, b"t", b"").strip(), "ascii")
t = str(get_arg(req, b"t", b"").strip(), "ascii")
replace = parse_replace_arg(get_arg(req, "replace", "true"))

if t == "mkdir":
Expand All @@ -283,7 +287,7 @@ def render_PUT(self, req):

@render_exception
def render_POST(self, req):
t = unicode(get_arg(req, b"t", b"").strip(), "ascii")
t = str(get_arg(req, b"t", b"").strip(), "ascii")

if t == "mkdir":
d = self._POST_mkdir(req)
Expand Down Expand Up @@ -372,21 +376,27 @@ def _POST_mkdir_immutable(self, req):
return d

def _POST_upload(self, req):
charset = unicode(get_arg(req, "_charset", b"utf-8"), "utf-8")
charset = str(get_arg(req, "_charset", b"utf-8"), "utf-8")
contents = req.fields["file"]
assert contents.filename is None or isinstance(contents.filename, str)
name = get_arg(req, "name")
name = name or contents.filename

# The filename embedded in the MIME file upload will be bytes on Python
# 2, Unicode on Python 3, or missing (i.e. None). The "name" field in
# the upload will be bytes on Python 2, Unicode on Python 3, or missing
# (i.e. None). We go through all these variations until we have a name
# that is Unicode.
assert contents.filename is None or isinstance(contents.filename, (bytes, str))
name = get_arg(req, "name") # returns bytes or None
name = name or contents.filename # unicode, bytes or None
if name is not None:
name = name.strip()
if not name:
# this prohibts empty, missing, and all-whitespace filenames
raise WebError("upload requires a name")
if isinstance(name, bytes):
name = name.decode(charset)
assert isinstance(name, str)
if "/" in name:
raise WebError("name= may not contain a slash", http.BAD_REQUEST)
assert isinstance(name, unicode)

# since POST /uri/path/file?t=upload is equivalent to
# POST /uri/path/dir?t=upload&name=foo, just do the same thing that
Expand Down Expand Up @@ -421,7 +431,7 @@ def _POST_uri(self, req):
name = get_arg(req, "name")
if not name:
raise WebError("set-uri requires a name")
charset = unicode(get_arg(req, "_charset", b"utf-8"), "ascii")
charset = str(get_arg(req, "_charset", b"utf-8"), "ascii")
name = name.decode(charset)
replace = parse_replace_arg(get_arg(req, "replace", "true"))

Expand All @@ -445,7 +455,7 @@ def _POST_unlink(self, req):
# without a name= field. For our own HTML this isn't a big
# deal, because we create the 'unlink' POST buttons ourselves.
name = b''
charset = unicode(get_arg(req, "_charset", b"utf-8"), "ascii")
charset = str(get_arg(req, "_charset", b"utf-8"), "ascii")
name = name.decode(charset)
d = self.node.delete(name)
d.addCallback(lambda res: "thing unlinked")
Expand All @@ -461,22 +471,22 @@ def _POST_rename(self, req):
return self._POST_relink(req)

def _POST_relink(self, req):
charset = unicode(get_arg(req, "_charset", b"utf-8"), "ascii")
charset = str(get_arg(req, "_charset", b"utf-8"), "ascii")
replace = parse_replace_arg(get_arg(req, "replace", "true"))

from_name = get_arg(req, "from_name")
if from_name is not None:
from_name = from_name.strip()
from_name = from_name.decode(charset)
assert isinstance(from_name, unicode)
assert isinstance(from_name, str)
else:
raise WebError("from_name= is required")

to_name = get_arg(req, "to_name")
if to_name is not None:
to_name = to_name.strip()
to_name = to_name.decode(charset)
assert isinstance(to_name, unicode)
assert isinstance(to_name, str)
else:
to_name = from_name

Expand All @@ -493,7 +503,7 @@ def _POST_relink(self, req):
if to_dir is not None and to_dir != self.node.get_write_uri():
to_dir = to_dir.strip()
to_dir = to_dir.decode(charset)
assert isinstance(to_dir, unicode)
assert isinstance(to_dir, str)
to_path = to_dir.split(u"/")
to_root = self.client.nodemaker.create_from_cap(to_bytes(to_path[0]))
if not IDirectoryNode.providedBy(to_root):
Expand Down Expand Up @@ -632,8 +642,8 @@ def _POST_set_children(self, req):
# TODO test handling of bad JSON
raise
cs = {}
for name, (file_or_dir, mddict) in children.items():
name = unicode(name) # json returns str *or* unicode
for name, (file_or_dir, mddict) in list(children.items()):
name = str(name) # json returns str *or* unicode
writecap = mddict.get('rw_uri')
if writecap is not None:
writecap = writecap.encode("utf-8")
Expand Down Expand Up @@ -705,7 +715,7 @@ def children(self, req, tag):

@renderer
def title(self, req, tag):
si_s = unicode(abbreviated_dirnode(self.node), "utf-8")
si_s = str(abbreviated_dirnode(self.node), "utf-8")
header = ["Tahoe-LAFS - Directory SI=%s" % si_s]
if self.node.is_unknown():
header.append(" (unknown)")
Expand All @@ -719,7 +729,7 @@ def title(self, req, tag):

@renderer
def header(self, req, tag):
si_s = unicode(abbreviated_dirnode(self.node), "utf-8")
si_s = str(abbreviated_dirnode(self.node), "utf-8")
header = ["Tahoe-LAFS Directory SI=", tags.span(si_s, class_="data-chars")]
if self.node.is_unknown():
header.append(" (unknown)")
Expand Down Expand Up @@ -1013,7 +1023,7 @@ def _directory_json_metadata(req, dirnode):
d = dirnode.list()
def _got(children):
kids = {}
for name, (childnode, metadata) in children.items():
for name, (childnode, metadata) in list(children.items()):
assert IFilesystemNode.providedBy(childnode), childnode
rw_uri = childnode.get_write_uri()
ro_uri = childnode.get_readonly_uri()
Expand Down Expand Up @@ -1077,13 +1087,13 @@ def __init__(self, original):

@renderer
def title(self, req, tag):
return tag("Directory SI={}".format(unicode(abbreviated_dirnode(self.original), "ascii")))
return tag("Directory SI={}".format(str(abbreviated_dirnode(self.original), "ascii")))

@renderer
def header(self, req, tag):
header = [
"Rename "
"in directory SI=%s" % unicode(abbreviated_dirnode(self.original), "ascii"),
"in directory SI=%s" % str(abbreviated_dirnode(self.original), "ascii"),
]

if self.original.is_readonly():
Expand Down Expand Up @@ -1194,7 +1204,7 @@ def _si_abbrev(self):
si = self.monitor.origin_si
if not si:
return "<LIT>"
return unicode(base32.b2a(si)[:6], "utf-8")
return str(base32.b2a(si)[:6], "utf-8")

@renderer
def title(self, req, tag):
Expand Down Expand Up @@ -1472,7 +1482,7 @@ def __init__(self, client, node, parentnode=None, name=None):

@render_exception
def render_GET(self, req):
t = unicode(get_arg(req, "t", "").strip(), "ascii")
t = str(get_arg(req, "t", "").strip(), "ascii")
if t == "info":
return MoreInfo(self.node)
if t == "json":
Expand Down
16 changes: 13 additions & 3 deletions src/allmydata/web/operations.py
@@ -1,4 +1,14 @@
from past.builtins import unicode
"""
Ported to Python 3.
"""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals

from future.utils import PY2
if PY2:
from future.builtins import filter, map, zip, ascii, chr, hex, input, next, oct, open, pow, round, super, bytes, dict, list, object, range, str, max, min # noqa: F401

import time
from hyperlink import (
Expand Down Expand Up @@ -91,7 +101,7 @@ def redirect_to(self, req):
"""
ophandle = get_arg(req, "ophandle").decode("utf-8")
assert ophandle
here = DecodedURL.from_text(unicode(URLPath.fromRequest(req)))
here = DecodedURL.from_text(str(URLPath.fromRequest(req)))
target = here.click(u"/").child(u"operations", ophandle)
output = get_arg(req, "output")
if output:
Expand All @@ -102,7 +112,7 @@ def redirect_to(self, req):
def getChild(self, name, req):
ophandle = name
if ophandle not in self.handles:
raise WebError("unknown/expired handle '%s'" % escape(unicode(ophandle, "utf-8")),
raise WebError("unknown/expired handle '%s'" % escape(str(ophandle, "utf-8")),
NOT_FOUND)
(monitor, renderer, when_added) = self.handles[ophandle]

Expand Down
18 changes: 11 additions & 7 deletions src/allmydata/web/private.py
@@ -1,10 +1,14 @@

from __future__ import (
print_function,
unicode_literals,
absolute_import,
division,
)
"""
Ported to Python 3.
"""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals

from future.utils import PY2
if PY2:
from future.builtins import filter, map, zip, ascii, chr, hex, input, next, oct, open, pow, round, super, bytes, dict, list, object, range, str, max, min # noqa: F401

import attr

Expand Down
17 changes: 13 additions & 4 deletions src/allmydata/web/root.py
@@ -1,5 +1,14 @@
from future.utils import PY3
from past.builtins import unicode
"""
Ported to Python 3.
"""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals

from future.utils import PY2, PY3
if PY2:
from future.builtins import filter, map, zip, ascii, chr, hex, input, next, oct, open, pow, round, super, bytes, dict, list, object, range, str, max, min # noqa: F401

import os
import time
Expand Down Expand Up @@ -98,7 +107,7 @@ def render_PUT(self, req):
either "PUT /uri" to create an unlinked file, or
"PUT /uri?t=mkdir" to create an unlinked directory
"""
t = unicode(get_arg(req, "t", "").strip(), "utf-8")
t = str(get_arg(req, "t", "").strip(), "utf-8")
if t == "":
file_format = get_format(req, "CHK")
mutable_type = get_mutable_type(file_format)
Expand All @@ -121,7 +130,7 @@ def render_POST(self, req):
unlinked file or "POST /uri?t=mkdir" to create a
new directory
"""
t = unicode(get_arg(req, "t", "").strip(), "ascii")
t = str(get_arg(req, "t", "").strip(), "ascii")
if t in ("", "upload"):
file_format = get_format(req)
mutable_type = get_mutable_type(file_format)
Expand Down

0 comments on commit e1ef9d4

Please sign in to comment.