Merge branch '3603.scripts' into 3634.cli-tests-python-3

This commit is contained in:
Itamar Turner-Trauring 2021-03-15 10:36:56 -04:00
commit 4d80177b18
10 changed files with 127 additions and 48 deletions

View File

View File

@ -1,8 +1,11 @@
# coding: utf-8
from __future__ import print_function from __future__ import print_function
import os, sys, urllib, textwrap import os, sys, textwrap
import codecs import codecs
from os.path import join from os.path import join
import urllib.parse
try: try:
from typing import Optional from typing import Optional
@ -270,6 +273,20 @@ def get_alias(aliases, path_unicode, default):
return uri.from_string_dirnode(aliases[alias]).to_string(), path[colon+1:] return uri.from_string_dirnode(aliases[alias]).to_string(), path[colon+1:]
def escape_path(path): def escape_path(path):
# this always returns bytes, specifically US-ASCII, valid URL characters # type: (str) -> str
u"""
Return path quoted to US-ASCII, valid URL characters.
>>> path = u'/føö/bar/☃'
>>> escaped = escape_path(path)
>>> str(escaped)
'/f%C3%B8%C3%B6/bar/%E2%98%83'
>>> escaped.encode('ascii').decode('ascii') == escaped
True
"""
segments = path.split("/") segments = path.split("/")
return "/".join([urllib.quote(unicode_to_url(s)) for s in segments]) result = "/".join([urllib.parse.quote(unicode_to_url(s)) for s in segments])
# fixme: test.cli.test_create_alias fails if it gets Unicode on Python 2
if PY2 and isinstance(result, type(u'')):
result = result.encode('ascii')
return result

View File

@ -1,5 +1,15 @@
from __future__ import print_function # Ported to Python 3
from __future__ import print_function
from __future__ import absolute_import
from __future__ import division
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 io
import os import os
import json import json
@ -225,7 +235,7 @@ class CreateIntroducerOptions(NoDefaultBasedirOptions):
@defer.inlineCallbacks @defer.inlineCallbacks
def write_node_config(c, config): def write_node_config(c, config):
# this is shared between clients and introducers # this is shared between clients and introducers
c.write("# -*- mode: conf; coding: utf-8 -*-\n") c.write("# -*- mode: conf; coding: {c.encoding} -*-\n".format(c=c))
c.write("\n") c.write("\n")
c.write("# This file controls the configuration of the Tahoe node that\n") c.write("# This file controls the configuration of the Tahoe node that\n")
c.write("# lives in this directory. It is only read at node startup.\n") c.write("# lives in this directory. It is only read at node startup.\n")
@ -244,7 +254,7 @@ def write_node_config(c, config):
c.write("[node]\n") c.write("[node]\n")
nickname = argv_to_unicode(config.get("nickname") or "") nickname = argv_to_unicode(config.get("nickname") or "")
c.write("nickname = %s\n" % (nickname.encode('utf-8'),)) c.write("nickname = %s\n" % (nickname,))
if config["hide-ip"]: if config["hide-ip"]:
c.write("reveal-IP-address = false\n") c.write("reveal-IP-address = false\n")
else: else:
@ -254,7 +264,7 @@ def write_node_config(c, config):
webport = argv_to_unicode(config.get("webport") or "none") webport = argv_to_unicode(config.get("webport") or "none")
if webport.lower() == "none": if webport.lower() == "none":
webport = "" webport = ""
c.write("web.port = %s\n" % (webport.encode('utf-8'),)) c.write("web.port = %s\n" % (webport,))
c.write("web.static = public_html\n") c.write("web.static = public_html\n")
listeners = config['listen'].split(",") listeners = config['listen'].split(",")
@ -279,15 +289,14 @@ def write_node_config(c, config):
tub_locations.append(i2p_location) tub_locations.append(i2p_location)
if "tcp" in listeners: if "tcp" in listeners:
if config["port"]: # --port/--location are a pair if config["port"]: # --port/--location are a pair
tub_ports.append(config["port"].encode('utf-8')) tub_ports.append(config["port"])
tub_locations.append(config["location"].encode('utf-8')) tub_locations.append(config["location"])
else: else:
assert "hostname" in config assert "hostname" in config
hostname = config["hostname"] hostname = config["hostname"]
new_port = iputil.allocate_tcp_port() new_port = iputil.allocate_tcp_port()
tub_ports.append("tcp:%s" % new_port) tub_ports.append("tcp:%s" % new_port)
tub_locations.append("tcp:%s:%s" % (hostname.encode('utf-8'), tub_locations.append("tcp:%s:%s" % (hostname, new_port))
new_port))
c.write("tub.port = %s\n" % ",".join(tub_ports)) c.write("tub.port = %s\n" % ",".join(tub_ports))
c.write("tub.location = %s\n" % ",".join(tub_locations)) c.write("tub.location = %s\n" % ",".join(tub_locations))
c.write("\n") c.write("\n")
@ -301,13 +310,13 @@ def write_node_config(c, config):
if tor_config: if tor_config:
c.write("[tor]\n") c.write("[tor]\n")
for key, value in tor_config.items(): for key, value in list(tor_config.items()):
c.write("%s = %s\n" % (key, value)) c.write("%s = %s\n" % (key, value))
c.write("\n") c.write("\n")
if i2p_config: if i2p_config:
c.write("[i2p]\n") c.write("[i2p]\n")
for key, value in i2p_config.items(): for key, value in list(i2p_config.items()):
c.write("%s = %s\n" % (key, value)) c.write("%s = %s\n" % (key, value))
c.write("\n") c.write("\n")
@ -370,7 +379,7 @@ def _get_config_via_wormhole(config):
relay_url=relay_url, relay_url=relay_url,
reactor=reactor, reactor=reactor,
) )
code = unicode(config['join']) code = str(config['join'])
wh.set_code(code) wh.set_code(code)
yield wh.get_welcome() yield wh.get_welcome()
print("Connected to wormhole server", file=out) print("Connected to wormhole server", file=out)
@ -402,7 +411,7 @@ def create_node(config):
err = config.stderr err = config.stderr
basedir = config['basedir'] basedir = config['basedir']
# This should always be called with an absolute Unicode basedir. # This should always be called with an absolute Unicode basedir.
precondition(isinstance(basedir, unicode), basedir) precondition(isinstance(basedir, str), basedir)
if os.path.exists(basedir): if os.path.exists(basedir):
if listdir_unicode(basedir): if listdir_unicode(basedir):
@ -437,7 +446,7 @@ def create_node(config):
v = remote_config.get(k, None) v = remote_config.get(k, None)
if v is not None: if v is not None:
# we're faking usually argv-supplied options :/ # we're faking usually argv-supplied options :/
if isinstance(v, unicode): if isinstance(v, str):
v = v.encode(get_io_encoding()) v = v.encode(get_io_encoding())
config[k] = v config[k] = v
if k not in sensitive_keys: if k not in sensitive_keys:
@ -447,7 +456,8 @@ def create_node(config):
print(" {}: [sensitive data; see tahoe.cfg]".format(k), file=out) print(" {}: [sensitive data; see tahoe.cfg]".format(k), file=out)
fileutil.make_dirs(os.path.join(basedir, "private"), 0o700) fileutil.make_dirs(os.path.join(basedir, "private"), 0o700)
with open(os.path.join(basedir, "tahoe.cfg"), "w") as c: cfg_name = os.path.join(basedir, "tahoe.cfg")
with io.open(cfg_name, "w", encoding='utf-8') as c:
yield write_node_config(c, config) yield write_node_config(c, config)
write_client_config(c, config) write_client_config(c, config)
@ -475,7 +485,7 @@ def create_introducer(config):
err = config.stderr err = config.stderr
basedir = config['basedir'] basedir = config['basedir']
# This should always be called with an absolute Unicode basedir. # This should always be called with an absolute Unicode basedir.
precondition(isinstance(basedir, unicode), basedir) precondition(isinstance(basedir, str), basedir)
if os.path.exists(basedir): if os.path.exists(basedir):
if listdir_unicode(basedir): if listdir_unicode(basedir):
@ -489,7 +499,8 @@ def create_introducer(config):
write_tac(basedir, "introducer") write_tac(basedir, "introducer")
fileutil.make_dirs(os.path.join(basedir, "private"), 0o700) fileutil.make_dirs(os.path.join(basedir, "private"), 0o700)
with open(os.path.join(basedir, "tahoe.cfg"), "w") as c: cfg_name = os.path.join(basedir, "tahoe.cfg")
with io.open(cfg_name, "w", encoding='utf-8') as c:
yield write_node_config(c, config) yield write_node_config(c, config)
print("Introducer created in %s" % quote_local_unicode_path(basedir), file=out) print("Introducer created in %s" % quote_local_unicode_path(basedir), file=out)

View File

@ -1,5 +1,6 @@
from __future__ import print_function from __future__ import print_function
import warnings
import os, sys import os, sys
from six.moves import StringIO from six.moves import StringIO
import six import six
@ -15,7 +16,7 @@ from twisted.internet import defer, task, threads
from allmydata.scripts.common import get_default_nodedir from allmydata.scripts.common import get_default_nodedir
from allmydata.scripts import debug, create_node, cli, \ from allmydata.scripts import debug, create_node, cli, \
admin, tahoe_run, tahoe_invite admin, tahoe_run, tahoe_invite
from allmydata.util.encodingutil import quote_output, quote_local_unicode_path, get_io_encoding from allmydata.util.encodingutil import quote_local_unicode_path
from allmydata.util.eliotutil import ( from allmydata.util.eliotutil import (
opt_eliot_destination, opt_eliot_destination,
opt_help_eliot_destinations, opt_help_eliot_destinations,
@ -120,11 +121,7 @@ def parse_or_exit_with_explanation(argv, stdout=sys.stdout):
while hasattr(c, 'subOptions'): while hasattr(c, 'subOptions'):
c = c.subOptions c = c.subOptions
print(str(c), file=stdout) print(str(c), file=stdout)
try: print("%s: %s\n" % (sys.argv[0], e), file=stdout)
msg = e.args[0].decode(get_io_encoding())
except Exception:
msg = repr(e)
print("%s: %s\n" % (sys.argv[0], quote_output(msg, quotemarks=False)), file=stdout)
sys.exit(1) sys.exit(1)
return config return config
@ -177,9 +174,8 @@ def _maybe_enable_eliot_logging(options, reactor):
return options return options
def run(): def run():
# TODO(3035): Remove tox-check when error becomes a warning if not six.PY2:
if 'TOX_ENV_NAME' not in os.environ: warnings.warn("Support for Python 3 is experimental. Use at your own risk.")
assert sys.version_info < (3,), u"Tahoe-LAFS does not run under Python 3. Please use Python 2.7.x."
if sys.platform == "win32": if sys.platform == "win32":
from allmydata.windows.fixups import initialize from allmydata.windows.fixups import initialize

View File

@ -1,3 +1,9 @@
"""
Type definitions used by modules in this package.
"""
# Python 3 only
from typing import List, Tuple, Type, Sequence, Any from typing import List, Tuple, Type, Sequence, Any
from allmydata.scripts.common import BaseOptions from allmydata.scripts.common import BaseOptions

View File

@ -1,3 +1,15 @@
"""
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 os import os
import mock import mock
from twisted.trial import unittest from twisted.trial import unittest

View File

@ -1,6 +1,6 @@
from __future__ import print_function from __future__ import print_function
from future.utils import PY2, native_str, bchr, binary_type from future.utils import PY2, bchr, binary_type
from future.builtins import str as future_str from future.builtins import str as future_str
from past.builtins import unicode from past.builtins import unicode
@ -20,7 +20,7 @@ from twisted.trial import unittest
from ..util.assertutil import precondition from ..util.assertutil import precondition
from ..scripts import runner from ..scripts import runner
from allmydata.util.encodingutil import unicode_platform, get_filesystem_encoding, get_io_encoding from allmydata.util.encodingutil import unicode_platform, get_filesystem_encoding, get_io_encoding, argv_type, unicode_to_argv
def skip_if_cannot_represent_filename(u): def skip_if_cannot_represent_filename(u):
@ -49,6 +49,13 @@ def _getvalue(io):
return io.read() return io.read()
def maybe_unicode_to_argv(o):
"""Convert object to argv form if necessary."""
if isinstance(o, unicode):
return unicode_to_argv(o)
return o
def run_cli_native(verb, *args, **kwargs): def run_cli_native(verb, *args, **kwargs):
""" """
Run a Tahoe-LAFS CLI command specified as bytes (on Python 2) or Unicode Run a Tahoe-LAFS CLI command specified as bytes (on Python 2) or Unicode
@ -74,9 +81,12 @@ def run_cli_native(verb, *args, **kwargs):
""" """
nodeargs = kwargs.pop("nodeargs", []) nodeargs = kwargs.pop("nodeargs", [])
encoding = kwargs.pop("encoding", None) encoding = kwargs.pop("encoding", None)
verb = maybe_unicode_to_argv(verb)
args = [maybe_unicode_to_argv(a) for a in args]
nodeargs = [maybe_unicode_to_argv(a) for a in nodeargs]
precondition( precondition(
all(isinstance(arg, native_str) for arg in [verb] + nodeargs + list(args)), all(isinstance(arg, argv_type) for arg in [verb] + nodeargs + list(args)),
"arguments to run_cli must be a native string -- convert using unicode_to_argv", "arguments to run_cli must be {argv_type} -- convert using unicode_to_argv".format(argv_type=argv_type),
verb=verb, verb=verb,
args=args, args=args,
nodeargs=nodeargs, nodeargs=nodeargs,

View File

@ -1,8 +1,17 @@
"""
Ported to Python 3
"""
from __future__ import ( from __future__ import (
absolute_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 os.path, re, sys import os.path, re, sys
from os import linesep from os import linesep
@ -10,6 +19,8 @@ from eliot import (
log_call, log_call,
) )
import six
from twisted.trial import unittest from twisted.trial import unittest
from twisted.internet import reactor from twisted.internet import reactor
@ -23,7 +34,7 @@ from twisted.python.runtime import (
platform, platform,
) )
from allmydata.util import fileutil, pollmixin from allmydata.util import fileutil, pollmixin
from allmydata.util.encodingutil import unicode_to_argv, get_filesystem_encoding from allmydata.util.encodingutil import unicode_to_argv
from allmydata.test import common_util from allmydata.test import common_util
import allmydata import allmydata
from .common import ( from .common import (
@ -72,7 +83,10 @@ def run_bintahoe(extra_argv, python_options=None):
:return: A three-tuple of stdout (unicode), stderr (unicode), and the :return: A three-tuple of stdout (unicode), stderr (unicode), and the
child process "returncode" (int). child process "returncode" (int).
""" """
argv = [sys.executable.decode(get_filesystem_encoding())] # fixme: below, 'unicode_to_argv' is called so ensure that
# executable is unicode to support that expectation.
executable = sys.executable.decode('utf-8') if six.PY2 else sys.executable
argv = [executable]
if python_options is not None: if python_options is not None:
argv.extend(python_options) argv.extend(python_options)
argv.extend([u"-m", u"allmydata.scripts.runner"]) argv.extend([u"-m", u"allmydata.scripts.runner"])
@ -167,7 +181,7 @@ class CreateNode(unittest.TestCase):
n1 = os.path.join(basedir, command + "-n1") n1 = os.path.join(basedir, command + "-n1")
argv = ["--quiet", command, "--basedir", n1] + list(args) argv = ["--quiet", command, "--basedir", n1] + list(args)
rc, out, err = yield run_cli(*argv) rc, out, err = yield run_cli(*map(unicode_to_argv, argv))
self.failUnlessEqual(err, "") self.failUnlessEqual(err, "")
self.failUnlessEqual(out, "") self.failUnlessEqual(out, "")
self.failUnlessEqual(rc, 0) self.failUnlessEqual(rc, 0)
@ -179,7 +193,7 @@ class CreateNode(unittest.TestCase):
# 'create-node', and disabled for 'create-client'. # 'create-node', and disabled for 'create-client'.
tahoe_cfg = os.path.join(n1, "tahoe.cfg") tahoe_cfg = os.path.join(n1, "tahoe.cfg")
self.failUnless(os.path.exists(tahoe_cfg)) self.failUnless(os.path.exists(tahoe_cfg))
content = fileutil.read(tahoe_cfg).replace('\r\n', '\n') content = fileutil.read(tahoe_cfg).decode('utf-8').replace('\r\n', '\n')
if kind == "client": if kind == "client":
self.failUnless(re.search(r"\n\[storage\]\n#.*\nenabled = false\n", content), content) self.failUnless(re.search(r"\n\[storage\]\n#.*\nenabled = false\n", content), content)
else: else:
@ -187,7 +201,7 @@ class CreateNode(unittest.TestCase):
self.failUnless("\nreserved_space = 1G\n" in content) self.failUnless("\nreserved_space = 1G\n" in content)
# creating the node a second time should be rejected # creating the node a second time should be rejected
rc, out, err = yield run_cli(*argv) rc, out, err = yield run_cli(*map(unicode_to_argv, argv))
self.failIfEqual(rc, 0, str((out, err, rc))) self.failIfEqual(rc, 0, str((out, err, rc)))
self.failUnlessEqual(out, "") self.failUnlessEqual(out, "")
self.failUnless("is not empty." in err) self.failUnless("is not empty." in err)
@ -200,7 +214,7 @@ class CreateNode(unittest.TestCase):
# test that the non --basedir form works too # test that the non --basedir form works too
n2 = os.path.join(basedir, command + "-n2") n2 = os.path.join(basedir, command + "-n2")
argv = ["--quiet", command] + list(args) + [n2] argv = ["--quiet", command] + list(args) + [n2]
rc, out, err = yield run_cli(*argv) rc, out, err = yield run_cli(*map(unicode_to_argv, argv))
self.failUnlessEqual(err, "") self.failUnlessEqual(err, "")
self.failUnlessEqual(out, "") self.failUnlessEqual(out, "")
self.failUnlessEqual(rc, 0) self.failUnlessEqual(rc, 0)
@ -210,7 +224,7 @@ class CreateNode(unittest.TestCase):
# test the --node-directory form # test the --node-directory form
n3 = os.path.join(basedir, command + "-n3") n3 = os.path.join(basedir, command + "-n3")
argv = ["--quiet", "--node-directory", n3, command] + list(args) argv = ["--quiet", "--node-directory", n3, command] + list(args)
rc, out, err = yield run_cli(*argv) rc, out, err = yield run_cli(*map(unicode_to_argv, argv))
self.failUnlessEqual(err, "") self.failUnlessEqual(err, "")
self.failUnlessEqual(out, "") self.failUnlessEqual(out, "")
self.failUnlessEqual(rc, 0) self.failUnlessEqual(rc, 0)
@ -221,7 +235,7 @@ class CreateNode(unittest.TestCase):
# test that the output (without --quiet) includes the base directory # test that the output (without --quiet) includes the base directory
n4 = os.path.join(basedir, command + "-n4") n4 = os.path.join(basedir, command + "-n4")
argv = [command] + list(args) + [n4] argv = [command] + list(args) + [n4]
rc, out, err = yield run_cli(*argv) rc, out, err = yield run_cli(*map(unicode_to_argv, argv))
self.failUnlessEqual(err, "") self.failUnlessEqual(err, "")
self.failUnlessIn(" created in ", out) self.failUnlessIn(" created in ", out)
self.failUnlessIn(n4, out) self.failUnlessIn(n4, out)
@ -291,7 +305,7 @@ class RunNode(common_util.SignalMixin, unittest.TestCase, pollmixin.PollMixin):
# This makes sure that node.url is written, which allows us to # This makes sure that node.url is written, which allows us to
# detect when the introducer restarts in _node_has_restarted below. # detect when the introducer restarts in _node_has_restarted below.
config = fileutil.read(tahoe.config_file.path) config = fileutil.read(tahoe.config_file.path).decode('utf-8')
self.assertIn('{}web.port = {}'.format(linesep, linesep), config) self.assertIn('{}web.port = {}'.format(linesep, linesep), config)
fileutil.write( fileutil.write(
tahoe.config_file.path, tahoe.config_file.path,
@ -303,7 +317,7 @@ class RunNode(common_util.SignalMixin, unittest.TestCase, pollmixin.PollMixin):
p = Expect() p = Expect()
tahoe.run(on_stdout(p)) tahoe.run(on_stdout(p))
yield p.expect("introducer running") yield p.expect(b"introducer running")
tahoe.active() tahoe.active()
yield self.poll(tahoe.introducer_furl_file.exists) yield self.poll(tahoe.introducer_furl_file.exists)
@ -326,7 +340,7 @@ class RunNode(common_util.SignalMixin, unittest.TestCase, pollmixin.PollMixin):
p = Expect() p = Expect()
tahoe.run(on_stdout(p)) tahoe.run(on_stdout(p))
yield p.expect("introducer running") yield p.expect(b"introducer running")
# Again, the second incarnation of the node might not be ready yet, so # Again, the second incarnation of the node might not be ready yet, so
# poll until it is. This time introducer_furl_file already exists, so # poll until it is. This time introducer_furl_file already exists, so
@ -369,7 +383,7 @@ class RunNode(common_util.SignalMixin, unittest.TestCase, pollmixin.PollMixin):
self.failUnlessEqual(returncode, 0) self.failUnlessEqual(returncode, 0)
# Check that the --webport option worked. # Check that the --webport option worked.
config = fileutil.read(tahoe.config_file.path) config = fileutil.read(tahoe.config_file.path).decode('utf-8')
self.assertIn( self.assertIn(
'{}web.port = 0{}'.format(linesep, linesep), '{}web.port = 0{}'.format(linesep, linesep),
config, config,
@ -382,7 +396,7 @@ class RunNode(common_util.SignalMixin, unittest.TestCase, pollmixin.PollMixin):
# This will run until we stop it. # This will run until we stop it.
tahoe.run(on_stdout(p)) tahoe.run(on_stdout(p))
# Wait for startup to have proceeded to a reasonable point. # Wait for startup to have proceeded to a reasonable point.
yield p.expect("client running") yield p.expect(b"client running")
tahoe.active() tahoe.active()
# read the storage.furl file so we can check that its contents don't # read the storage.furl file so we can check that its contents don't
@ -401,7 +415,7 @@ class RunNode(common_util.SignalMixin, unittest.TestCase, pollmixin.PollMixin):
# We don't have to add another cleanup for this one, the one from # We don't have to add another cleanup for this one, the one from
# above is still registered. # above is still registered.
tahoe.run(on_stdout(p)) tahoe.run(on_stdout(p))
yield p.expect("client running") yield p.expect(b"client running")
tahoe.active() tahoe.active()
self.assertEqual( self.assertEqual(
@ -492,7 +506,7 @@ class RunNode(common_util.SignalMixin, unittest.TestCase, pollmixin.PollMixin):
client_running = p.expect(b"client running") client_running = p.expect(b"client running")
result, index = yield DeferredList([ result, index = yield DeferredList([
p.expect(expected_message), p.expect(expected_message.encode('utf-8')),
client_running, client_running,
], fireOnOneCallback=True, consumeErrors=True, ], fireOnOneCallback=True, consumeErrors=True,
) )

View File

@ -75,6 +75,8 @@ PORTED_MODULES = [
"allmydata.mutable.servermap", "allmydata.mutable.servermap",
"allmydata.node", "allmydata.node",
"allmydata.nodemaker", "allmydata.nodemaker",
"allmydata.scripts.create_node",
"allmydata.scripts.types_",
"allmydata.stats", "allmydata.stats",
"allmydata.storage_client", "allmydata.storage_client",
"allmydata.storage.common", "allmydata.storage.common",
@ -137,7 +139,8 @@ PORTED_MODULES = [
PORTED_TEST_MODULES = [ PORTED_TEST_MODULES = [
"allmydata.test.cli.test_alias", "allmydata.test.cli.test_alias",
"allmydata.test.cli.test_create",
"allmydata.test.mutable.test_checker", "allmydata.test.mutable.test_checker",
"allmydata.test.mutable.test_datahandle", "allmydata.test.mutable.test_datahandle",
"allmydata.test.mutable.test_different_encoding", "allmydata.test.mutable.test_different_encoding",
@ -194,6 +197,7 @@ PORTED_TEST_MODULES = [
"allmydata.test.test_pipeline", "allmydata.test.test_pipeline",
"allmydata.test.test_python3", "allmydata.test.test_python3",
"allmydata.test.test_repairer", "allmydata.test.test_repairer",
"allmydata.test.test_runner",
"allmydata.test.test_sftp", "allmydata.test.test_sftp",
"allmydata.test.test_spans", "allmydata.test.test_spans",
"allmydata.test.test_statistics", "allmydata.test.test_statistics",

View File

@ -13,6 +13,7 @@ from __future__ import print_function
from __future__ import unicode_literals from __future__ import unicode_literals
from future.utils import PY2, PY3, native_str from future.utils import PY2, PY3, native_str
from future.builtins import str as future_str
if PY2: if PY2:
# We omit str() because that seems too tricky to get right. # We omit str() because that seems too tricky to get right.
from builtins import filter, map, zip, ascii, chr, hex, input, next, oct, open, pow, round, super, bytes, dict, list, object, range, max, min # noqa: F401 from builtins import filter, map, zip, ascii, chr, hex, input, next, oct, open, pow, round, super, bytes, dict, list, object, range, max, min # noqa: F401
@ -142,6 +143,14 @@ def unicode_to_argv(s, mangle=False):
return ensure_str(s) return ensure_str(s)
# According to unicode_to_argv above, the expected type for
# cli args depends on the platform, so capture that expectation.
argv_type = (future_str, native_str) if sys.platform == "win32" else native_str
"""
The expected type for args to a subprocess
"""
def unicode_to_url(s): def unicode_to_url(s):
""" """
Encode an unicode object used in an URL to bytes. Encode an unicode object used in an URL to bytes.