From 135ade02b159a8bde5c9083797d10720baf5c2f8 Mon Sep 17 00:00:00 2001 From: Itamar Turner-Trauring <itamar@itamarst.org> Date: Fri, 24 Jul 2020 13:47:56 -0400 Subject: [PATCH 01/21] Start porting. --- src/allmydata/test/test_iputil.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/allmydata/test/test_iputil.py b/src/allmydata/test/test_iputil.py index ebb77c3c8..7f26a53fb 100644 --- a/src/allmydata/test/test_iputil.py +++ b/src/allmydata/test/test_iputil.py @@ -10,7 +10,9 @@ from allmydata.util.namespace import Namespace DOTTED_QUAD_RE=re.compile("^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$") -MOCK_IPADDR_OUTPUT = """\ +# Mock output from subprocesses should be bytes, that's what happens on both +# Python 2 and Python 3: +MOCK_IPADDR_OUTPUT = b"""\ 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 16436 qdisc noqueue state UNKNOWN \n\ link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo @@ -28,7 +30,7 @@ MOCK_IPADDR_OUTPUT = """\ valid_lft forever preferred_lft forever """ -MOCK_IFCONFIG_OUTPUT = """\ +MOCK_IFCONFIG_OUTPUT = b"""\ eth1 Link encap:Ethernet HWaddr d4:3d:7e:01:b4:3e \n\ inet addr:192.168.0.6 Bcast:192.168.0.255 Mask:255.255.255.0 inet6 addr: fe80::d63d:7eff:fe01:b43e/64 Scope:Link @@ -59,7 +61,7 @@ wlan0 Link encap:Ethernet HWaddr 90:f6:52:27:15:0a \n\ """ # This is actually from a VirtualBox VM running XP. -MOCK_ROUTE_OUTPUT = """\ +MOCK_ROUTE_OUTPUT = b"""\ =========================================================================== Interface List 0x1 ........................... MS TCP Loopback interface @@ -98,6 +100,11 @@ class ListAddresses(testutil.SignalMixin, unittest.TestCase): def test_get_local_ip_for(self): addr = iputil.get_local_ip_for('127.0.0.1') self.failUnless(DOTTED_QUAD_RE.match(addr)) + # Bytes can be taken as input: + bytes_addr = iputil.get_local_ip_for(b'127.0.0.1') + self.assertEqual(addr, bytes_addr) + # The output is a native string: + self.assertIsInstance(addr, str) def test_list_async(self): d = iputil.get_local_addresses_async() From 45a891114b033306dcbb66b3310d5d601970f628 Mon Sep 17 00:00:00 2001 From: Itamar Turner-Trauring <itamar@itamarst.org> Date: Fri, 24 Jul 2020 13:48:11 -0400 Subject: [PATCH 02/21] Fix some things that prevent Python 3 imports. --- src/allmydata/util/encodingutil.py | 3 ++- src/allmydata/util/fileutil.py | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/allmydata/util/encodingutil.py b/src/allmydata/util/encodingutil.py index 568267b35..b31fd1205 100644 --- a/src/allmydata/util/encodingutil.py +++ b/src/allmydata/util/encodingutil.py @@ -4,7 +4,6 @@ unicode and back. """ import sys, os, re, locale -from types import NoneType from allmydata.util.assertutil import precondition, _assert from twisted.python import usage @@ -12,6 +11,8 @@ from twisted.python.filepath import FilePath from allmydata.util import log from allmydata.util.fileutil import abspath_expanduser_unicode +NoneType = type(None) + def canonical_encoding(encoding): if encoding is None: diff --git a/src/allmydata/util/fileutil.py b/src/allmydata/util/fileutil.py index 269a8a356..36946ff02 100644 --- a/src/allmydata/util/fileutil.py +++ b/src/allmydata/util/fileutil.py @@ -4,7 +4,7 @@ from __future__ import print_function Futz with files like a pro. """ -import sys, exceptions, os, stat, tempfile, time, binascii +import sys, os, stat, tempfile, time, binascii import six from collections import namedtuple from errno import ENOENT @@ -190,7 +190,7 @@ def make_dirs(dirname, mode=0o777): if not os.path.isdir(dirname): if tx: raise tx - raise exceptions.IOError("unknown error prevented creation of directory, or deleted the directory immediately after creation: %s" % dirname) # careful not to construct an IOError with a 2-tuple, as that has a special meaning... + raise IOError("unknown error prevented creation of directory, or deleted the directory immediately after creation: %s" % dirname) # careful not to construct an IOError with a 2-tuple, as that has a special meaning... def rm_dir(dirname): """ From bf134019797b2fadc8f92405720b1956e4b2fbc5 Mon Sep 17 00:00:00 2001 From: Itamar Turner-Trauring <itamar@itamarst.org> Date: Fri, 24 Jul 2020 14:58:08 -0400 Subject: [PATCH 03/21] Move SignalMixin to common_py3.py. --- src/allmydata/test/common_py3.py | 25 +++++++++++++++++++++++++ src/allmydata/test/common_util.py | 23 ++--------------------- src/allmydata/test/test_iputil.py | 2 +- 3 files changed, 28 insertions(+), 22 deletions(-) diff --git a/src/allmydata/test/common_py3.py b/src/allmydata/test/common_py3.py index e6303d2f2..97745e293 100644 --- a/src/allmydata/test/common_py3.py +++ b/src/allmydata/test/common_py3.py @@ -15,6 +15,9 @@ if PY2: import os import time +import signal + +from twisted.internet import reactor class TimezoneMixin(object): @@ -40,3 +43,25 @@ class TimezoneMixin(object): def have_working_tzset(self): return hasattr(time, 'tzset') + + +class SignalMixin(object): + # This class is necessary for any code which wants to use Processes + # outside the usual reactor.run() environment. It is copied from + # Twisted's twisted.test.test_process . Note that Twisted-8.2.0 uses + # something rather different. + sigchldHandler = None + + def setUp(self): + # make sure SIGCHLD handler is installed, as it should be on + # reactor.run(). problem is reactor may not have been run when this + # test runs. + if hasattr(reactor, "_handleSigchld") and hasattr(signal, "SIGCHLD"): + self.sigchldHandler = signal.signal(signal.SIGCHLD, + reactor._handleSigchld) + return super(SignalMixin, self).setUp() + + def tearDown(self): + if self.sigchldHandler: + signal.signal(signal.SIGCHLD, self.sigchldHandler) + return super(SignalMixin, self).tearDown() diff --git a/src/allmydata/test/common_util.py b/src/allmydata/test/common_util.py index 996052692..83f37a11d 100644 --- a/src/allmydata/test/common_util.py +++ b/src/allmydata/test/common_util.py @@ -12,6 +12,8 @@ from ..util.assertutil import precondition from allmydata.util.encodingutil import (unicode_platform, get_filesystem_encoding, get_io_encoding) from ..scripts import runner +from .common_py3 import SignalMixin + def skip_if_cannot_represent_filename(u): precondition(isinstance(u, unicode)) @@ -88,27 +90,6 @@ class ReallyEqualMixin(object): self.assertEqual(type(a), type(b), "a :: %r, b :: %r, %r" % (a, b, msg)) -class SignalMixin(object): - # This class is necessary for any code which wants to use Processes - # outside the usual reactor.run() environment. It is copied from - # Twisted's twisted.test.test_process . Note that Twisted-8.2.0 uses - # something rather different. - sigchldHandler = None - - def setUp(self): - # make sure SIGCHLD handler is installed, as it should be on - # reactor.run(). problem is reactor may not have been run when this - # test runs. - if hasattr(reactor, "_handleSigchld") and hasattr(signal, "SIGCHLD"): - self.sigchldHandler = signal.signal(signal.SIGCHLD, - reactor._handleSigchld) - return super(SignalMixin, self).setUp() - - def tearDown(self): - if self.sigchldHandler: - signal.signal(signal.SIGCHLD, self.sigchldHandler) - return super(SignalMixin, self).tearDown() - class StallMixin(object): def stall(self, res=None, delay=1): d = defer.Deferred() diff --git a/src/allmydata/test/test_iputil.py b/src/allmydata/test/test_iputil.py index 7f26a53fb..0c0992430 100644 --- a/src/allmydata/test/test_iputil.py +++ b/src/allmydata/test/test_iputil.py @@ -4,7 +4,7 @@ import re, errno, subprocess, os from twisted.trial import unittest from allmydata.util import iputil -import allmydata.test.common_util as testutil +import allmydata.test.common_py3 as testutil from allmydata.util.namespace import Namespace From 1f34e6298790c6ff9db53743a3a0b441ffaa1313 Mon Sep 17 00:00:00 2001 From: Itamar Turner-Trauring <itamar@itamarst.org> Date: Mon, 27 Jul 2020 11:14:01 -0400 Subject: [PATCH 04/21] Port test module to Python 3. --- src/allmydata/test/test_iputil.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/allmydata/test/test_iputil.py b/src/allmydata/test/test_iputil.py index 0c0992430..1113e3540 100644 --- a/src/allmydata/test/test_iputil.py +++ b/src/allmydata/test/test_iputil.py @@ -1,3 +1,11 @@ +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +from future.utils import PY2, native_str +if PY2: + from builtins import filter, map, zip, ascii, chr, hex, input, next, oct, open, pow, round, super, bytes, dict, int, list, object, range, str, max, min # noqa: F401 import re, errno, subprocess, os @@ -8,7 +16,7 @@ import allmydata.test.common_py3 as testutil from allmydata.util.namespace import Namespace -DOTTED_QUAD_RE=re.compile("^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$") +DOTTED_QUAD_RE=re.compile(r"^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$") # Mock output from subprocesses should be bytes, that's what happens on both # Python 2 and Python 3: @@ -104,7 +112,7 @@ class ListAddresses(testutil.SignalMixin, unittest.TestCase): bytes_addr = iputil.get_local_ip_for(b'127.0.0.1') self.assertEqual(addr, bytes_addr) # The output is a native string: - self.assertIsInstance(addr, str) + self.assertIsInstance(addr, native_str) def test_list_async(self): d = iputil.get_local_addresses_async() From fce7221481fd582a904bdb16ea3b81b57dfac970 Mon Sep 17 00:00:00 2001 From: Itamar Turner-Trauring <itamar@itamarst.org> Date: Mon, 27 Jul 2020 11:42:20 -0400 Subject: [PATCH 05/21] Some steps to manually port to Python 3. --- src/allmydata/util/iputil.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/allmydata/util/iputil.py b/src/allmydata/util/iputil.py index 57a7e790a..71dded28f 100644 --- a/src/allmydata/util/iputil.py +++ b/src/allmydata/util/iputil.py @@ -171,11 +171,11 @@ def get_local_ip_for(target): # ... thus wrote Greg Smith in time immemorial... # Also, the Win32 APIs for this are really klunky and error-prone. --Daira -_win32_re = re.compile(r'^\s*\d+\.\d+\.\d+\.\d+\s.+\s(?P<address>\d+\.\d+\.\d+\.\d+)\s+(?P<metric>\d+)\s*$', flags=re.M|re.I|re.S) +_win32_re = re.compile(br'^\s*\d+\.\d+\.\d+\.\d+\s.+\s(?P<address>\d+\.\d+\.\d+\.\d+)\s+(?P<metric>\d+)\s*$', flags=re.M|re.I|re.S) _win32_commands = (('route.exe', ('print',), _win32_re),) # These work in most Unices. -_addr_re = re.compile(r'^\s*inet [a-zA-Z]*:?(?P<address>\d+\.\d+\.\d+\.\d+)[\s/].+$', flags=re.M|re.I|re.S) +_addr_re = re.compile(br'^\s*inet [a-zA-Z]*:?(?P<address>\d+\.\d+\.\d+\.\d+)[\s/].+$', flags=re.M|re.I|re.S) _unix_commands = (('/bin/ip', ('addr',), _addr_re), ('/sbin/ip', ('addr',), _addr_re), ('/sbin/ifconfig', ('-a',), _addr_re), @@ -209,10 +209,13 @@ def _synchronously_find_addresses_via_config(): else: exes_to_try = which(pathtotool) + subprocess_error = getattr( + subprocess, "SubprocessError", subprocess.CalledProcessError + ) for exe in exes_to_try: try: addresses = _query(exe, args, regex) - except Exception: + except (IOError, OSError, ValueError, subprocess_error): addresses = [] if addresses: return addresses @@ -224,7 +227,7 @@ def _query(path, args, regex): return [] env = {'LANG': 'en_US.UTF-8'} TRIES = 5 - for trial in xrange(TRIES): + for trial in range(TRIES): try: p = subprocess.Popen([path] + list(args), stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env) (output, err) = p.communicate() @@ -235,13 +238,13 @@ def _query(path, args, regex): raise addresses = [] - outputsplit = output.split('\n') + outputsplit = output.split(b'\n') for outline in outputsplit: m = regex.match(outline) if m: addr = m.group('address') if addr not in addresses: - addresses.append(addr) + addresses.append(addr.decode("utf-8")) return addresses From 436c2e77e422370c81ccdb650816ca8cd48ecca0 Mon Sep 17 00:00:00 2001 From: Itamar Turner-Trauring <itamar@itamarst.org> Date: Mon, 27 Jul 2020 11:44:58 -0400 Subject: [PATCH 06/21] Automated port to Python 3. --- src/allmydata/util/iputil.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/allmydata/util/iputil.py b/src/allmydata/util/iputil.py index 71dded28f..4623dbba6 100644 --- a/src/allmydata/util/iputil.py +++ b/src/allmydata/util/iputil.py @@ -1,4 +1,12 @@ -# from the Python Standard Library +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +from future.utils import PY2, native_str +if PY2: + from builtins import filter, map, zip, ascii, chr, hex, input, next, oct, open, pow, round, super, bytes, dict, int, list, object, range, str, max, min # noqa: F401 + import os, re, socket, subprocess, errno from sys import platform From 9919d2c9a7fcb2887541d887d94daf2db8b08275 Mon Sep 17 00:00:00 2001 From: Itamar Turner-Trauring <itamar@itamarst.org> Date: Mon, 27 Jul 2020 11:46:03 -0400 Subject: [PATCH 07/21] Record port status. --- src/allmydata/test/test_iputil.py | 6 ++++++ src/allmydata/util/_python3.py | 2 ++ src/allmydata/util/iputil.py | 6 ++++++ 3 files changed, 14 insertions(+) diff --git a/src/allmydata/test/test_iputil.py b/src/allmydata/test/test_iputil.py index 1113e3540..3fac776c2 100644 --- a/src/allmydata/test/test_iputil.py +++ b/src/allmydata/test/test_iputil.py @@ -1,3 +1,9 @@ +""" +Tests for allmydata.util.iputil. + +Ported to Python 3. +""" + from __future__ import absolute_import from __future__ import division from __future__ import print_function diff --git a/src/allmydata/util/_python3.py b/src/allmydata/util/_python3.py index ff6916a72..d939d08b3 100644 --- a/src/allmydata/util/_python3.py +++ b/src/allmydata/util/_python3.py @@ -23,6 +23,7 @@ PORTED_MODULES = [ "allmydata.util.deferredutil", "allmydata.util.hashutil", "allmydata.util.humanreadable", + "allmydata.util.iputil", "allmydata.util.mathutil", "allmydata.util.namespace", "allmydata.util.netstring", @@ -42,6 +43,7 @@ PORTED_TEST_MODULES = [ "allmydata.test.test_hashtree", "allmydata.test.test_hashutil", "allmydata.test.test_humanreadable", + "allmydata.test.test_iputil", "allmydata.test.test_netstring", "allmydata.test.test_observer", "allmydata.test.test_pipeline", diff --git a/src/allmydata/util/iputil.py b/src/allmydata/util/iputil.py index 4623dbba6..947430e35 100644 --- a/src/allmydata/util/iputil.py +++ b/src/allmydata/util/iputil.py @@ -1,3 +1,9 @@ +""" +Utilities for getting IP addresses. + +Ported to Python 3. +""" + from __future__ import absolute_import from __future__ import division from __future__ import print_function From f9bda5bbd3975746dda392b636b80eb372d56370 Mon Sep 17 00:00:00 2001 From: Itamar Turner-Trauring <itamar@itamarst.org> Date: Mon, 27 Jul 2020 11:46:22 -0400 Subject: [PATCH 08/21] News file. --- newsfragments/3356.minor | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 newsfragments/3356.minor diff --git a/newsfragments/3356.minor b/newsfragments/3356.minor new file mode 100644 index 000000000..e69de29bb From 817355d17e98fffe6bb5ee9a6e8cc03e6081de93 Mon Sep 17 00:00:00 2001 From: Itamar Turner-Trauring <itamar@itamarst.org> Date: Mon, 27 Jul 2020 13:06:41 -0400 Subject: [PATCH 09/21] Minimal testing for listenOnUnused. --- src/allmydata/test/test_iputil.py | 33 ++++++++++++++++++++++++++++++- src/allmydata/util/iputil.py | 1 + 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/src/allmydata/test/test_iputil.py b/src/allmydata/test/test_iputil.py index 3fac776c2..a058da299 100644 --- a/src/allmydata/test/test_iputil.py +++ b/src/allmydata/test/test_iputil.py @@ -13,10 +13,12 @@ from future.utils import PY2, native_str if PY2: from builtins import filter, map, zip, ascii, chr, hex, input, next, oct, open, pow, round, super, bytes, dict, int, list, object, range, str, max, min # noqa: F401 -import re, errno, subprocess, os +import re, errno, subprocess, os, socket from twisted.trial import unittest +from foolscap.api import Tub + from allmydata.util import iputil import allmydata.test.common_py3 as testutil from allmydata.util.namespace import Namespace @@ -183,3 +185,32 @@ class ListAddresses(testutil.SignalMixin, unittest.TestCase): def test_list_async_mock_cygwin(self): self.patch(iputil, 'platform', "cygwin") return self._test_list_async_mock(None, None, CYGWIN_TEST_ADDRESSES) + + +class ListenOnUsed(unittest.TestCase): + """Tests for listenOnUnused.""" + + def create_tub(self, basedir): + os.makedirs(basedir) + tubfile = os.path.join(basedir, "tub.pem") + tub = Tub(certFile=tubfile) + tub.setOption("expose-remote-exception-types", False) + tub.startService() + self.addCleanup(tub.stopService) + return tub + + def test_random_port(self): + """A random port is selected if none is given.""" + tub = self.create_tub("utils/ListenOnUsed/test_randomport") + self.assertEqual(len(tub.getListeners()), 0) + portnum = iputil.listenOnUnused(tub) + # We can connect to this port: + s = socket.socket() + s.connect(("127.0.0.1", portnum)) + s.close() + self.assertEqual(len(tub.getListeners()), 1) + + # Listen on another port: + tub2 = self.create_tub("utils/ListenOnUsed/test_randomport_2") + portnum2 = iputil.listenOnUnused(tub2) + self.assertNotEqual(portnum, portnum2) diff --git a/src/allmydata/util/iputil.py b/src/allmydata/util/iputil.py index 947430e35..6a9639280 100644 --- a/src/allmydata/util/iputil.py +++ b/src/allmydata/util/iputil.py @@ -379,4 +379,5 @@ __all__ = ["allocate_tcp_port", "get_local_addresses_sync", "get_local_addresses_async", "get_local_ip_for", + "listenOnUnused", ] From 0071c1a48d0fa7ade416216456ff9f1c5446ef14 Mon Sep 17 00:00:00 2001 From: Itamar Turner-Trauring <itamar@itamarst.org> Date: Mon, 27 Jul 2020 13:08:25 -0400 Subject: [PATCH 10/21] More passing tests. --- misc/python3/ratchet-passing | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/misc/python3/ratchet-passing b/misc/python3/ratchet-passing index 6832b7fa7..bbe594998 100644 --- a/misc/python3/ratchet-passing +++ b/misc/python3/ratchet-passing @@ -44,6 +44,13 @@ allmydata.test.test_hashutil.HashUtilTests.test_sha256d allmydata.test.test_hashutil.HashUtilTests.test_sha256d_truncated allmydata.test.test_hashutil.HashUtilTests.test_timing_safe_compare allmydata.test.test_humanreadable.HumanReadable.test_repr +allmydata.test.test_iputil.ListAddresses.test_get_local_ip_for +allmydata.test.test_iputil.ListAddresses.test_list_async +allmydata.test.test_iputil.ListAddresses.test_list_async_mock_cygwin +allmydata.test.test_iputil.ListAddresses.test_list_async_mock_ifconfig +allmydata.test.test_iputil.ListAddresses.test_list_async_mock_ip_addr +allmydata.test.test_iputil.ListAddresses.test_list_async_mock_route +allmydata.test.test_iputil.ListenOnUsed.test_random_port allmydata.test.test_netstring.Netstring.test_encode allmydata.test.test_netstring.Netstring.test_extra allmydata.test.test_netstring.Netstring.test_nested From d91b4f0e40ef6359213c73927e0102b8ce338f53 Mon Sep 17 00:00:00 2001 From: Itamar Turner-Trauring <itamar@itamarst.org> Date: Mon, 27 Jul 2020 15:12:09 -0400 Subject: [PATCH 11/21] Fix some tests and flake issues. --- src/allmydata/test/common_util.py | 2 +- src/allmydata/util/iputil.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/allmydata/test/common_util.py b/src/allmydata/test/common_util.py index 83f37a11d..261d39af3 100644 --- a/src/allmydata/test/common_util.py +++ b/src/allmydata/test/common_util.py @@ -1,6 +1,6 @@ from __future__ import print_function -import os, signal +import os from random import randrange from six.moves import StringIO diff --git a/src/allmydata/util/iputil.py b/src/allmydata/util/iputil.py index 6a9639280..940f215db 100644 --- a/src/allmydata/util/iputil.py +++ b/src/allmydata/util/iputil.py @@ -370,7 +370,7 @@ def listenOnUnused(tub, portnum=None): """ portnum, endpoint = _foolscapEndpointForPortNumber(portnum) tub.listenOn(endpoint) - tub.setLocation("localhost:%d" % (portnum,)) + tub.setLocation(native_str("localhost:%d" % (portnum,))) return portnum From ce8a57580846d659eb680c1f9efa429cabe3402d Mon Sep 17 00:00:00 2001 From: Itamar Turner-Trauring <itamar@itamarst.org> Date: Mon, 27 Jul 2020 15:16:25 -0400 Subject: [PATCH 12/21] Test for specific ports. --- src/allmydata/test/test_iputil.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/allmydata/test/test_iputil.py b/src/allmydata/test/test_iputil.py index a058da299..42677af2f 100644 --- a/src/allmydata/test/test_iputil.py +++ b/src/allmydata/test/test_iputil.py @@ -214,3 +214,13 @@ class ListenOnUsed(unittest.TestCase): tub2 = self.create_tub("utils/ListenOnUsed/test_randomport_2") portnum2 = iputil.listenOnUnused(tub2) self.assertNotEqual(portnum, portnum2) + + def test_specific_port(self): + """The given port is used.""" + tub = self.create_tub("utils/ListenOnUsed/test_givenport") + s = socket.socket() + s.bind(("127.0.0.1", 0)) + port = s.getsockname()[1] + s.close() + port2 = iputil.listenOnUnused(tub, port) + self.assertEqual(port, port2) From 74fe9ccf56a36902ff884bc298b4a85d4d4fc545 Mon Sep 17 00:00:00 2001 From: Itamar Turner-Trauring <itamar@itamarst.org> Date: Mon, 27 Jul 2020 15:16:34 -0400 Subject: [PATCH 13/21] Foolscap expects a native string. --- src/allmydata/util/iputil.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/allmydata/util/iputil.py b/src/allmydata/util/iputil.py index 940f215db..af982e908 100644 --- a/src/allmydata/util/iputil.py +++ b/src/allmydata/util/iputil.py @@ -321,7 +321,7 @@ def _foolscapEndpointForPortNumber(portnum): # approach is error prone for the reasons described on # https://tahoe-lafs.org/trac/tahoe-lafs/ticket/2787 portnum = allocate_tcp_port() - return (portnum, "tcp:%d" % (portnum,)) + return (portnum, native_str("tcp:%d" % (portnum,))) @implementer(IStreamServerEndpoint) From d8bf811b92acaef6510e3e7503fb8424c2f57d5b Mon Sep 17 00:00:00 2001 From: Itamar Turner-Trauring <itamar@itamarst.org> Date: Mon, 27 Jul 2020 15:20:35 -0400 Subject: [PATCH 14/21] Native string all the things. --- src/allmydata/util/iputil.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/allmydata/util/iputil.py b/src/allmydata/util/iputil.py index af982e908..921c481ff 100644 --- a/src/allmydata/util/iputil.py +++ b/src/allmydata/util/iputil.py @@ -102,7 +102,7 @@ except ImportError: increase_rlimits = _increase_rlimits def get_local_addresses_sync(): - return _synchronously_find_addresses_via_config() + return [native_str(a) for a in _synchronously_find_addresses_via_config()] def get_local_addresses_async(target="198.41.0.4"): # A.ROOT-SERVERS.NET """ @@ -131,7 +131,7 @@ def get_local_addresses_async(target="198.41.0.4"): # A.ROOT-SERVERS.NET addresses.append(addr) return addresses d.addCallback(_collect) - + d.addCallback(lambda addresses: [native_str(s) for s in addresses]) return d def get_local_ip_for(target): @@ -176,7 +176,7 @@ def get_local_ip_for(target): except (socket.error, CannotListenError): # no route to that host localip = None - return localip + return native_str(localip) # Wow, I'm really amazed at home much mileage we've gotten out of calling From 94d489ff46883279f2994c03ca316f0c82f490d5 Mon Sep 17 00:00:00 2001 From: Itamar Turner-Trauring <itamar@itamarst.org> Date: Mon, 27 Jul 2020 16:01:12 -0400 Subject: [PATCH 15/21] Fix trailing whitespace. --- src/allmydata/util/iputil.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/allmydata/util/iputil.py b/src/allmydata/util/iputil.py index 921c481ff..ac5367cfc 100644 --- a/src/allmydata/util/iputil.py +++ b/src/allmydata/util/iputil.py @@ -131,7 +131,7 @@ def get_local_addresses_async(target="198.41.0.4"): # A.ROOT-SERVERS.NET addresses.append(addr) return addresses d.addCallback(_collect) - d.addCallback(lambda addresses: [native_str(s) for s in addresses]) + d.addCallback(lambda addresses: [native_str(s) for s in addresses]) return d def get_local_ip_for(target): From cbbe260a4eafef224f3bf06f599030a463f0c7c0 Mon Sep 17 00:00:00 2001 From: Itamar Turner-Trauring <itamar@itamarst.org> Date: Mon, 27 Jul 2020 16:19:36 -0400 Subject: [PATCH 16/21] Windows doesn't like Unicode strings in os.environ in Python 2.7. --- src/allmydata/util/iputil.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/allmydata/util/iputil.py b/src/allmydata/util/iputil.py index ac5367cfc..36f4bb79b 100644 --- a/src/allmydata/util/iputil.py +++ b/src/allmydata/util/iputil.py @@ -239,7 +239,7 @@ def _synchronously_find_addresses_via_config(): def _query(path, args, regex): if not os.path.isfile(path): return [] - env = {'LANG': 'en_US.UTF-8'} + env = {native_str('LANG'): native_str('en_US.UTF-8')} TRIES = 5 for trial in range(TRIES): try: From 4fdcd06fbdb4ce1e2dc7d3ea10ccccb70ca5eea2 Mon Sep 17 00:00:00 2001 From: Itamar Turner-Trauring <itamar@itamarst.org> Date: Tue, 28 Jul 2020 10:48:17 -0400 Subject: [PATCH 17/21] Another passing test for the ratchet. --- misc/python3/ratchet-passing | 1 + 1 file changed, 1 insertion(+) diff --git a/misc/python3/ratchet-passing b/misc/python3/ratchet-passing index bbe594998..68f1a968b 100644 --- a/misc/python3/ratchet-passing +++ b/misc/python3/ratchet-passing @@ -51,6 +51,7 @@ allmydata.test.test_iputil.ListAddresses.test_list_async_mock_ifconfig allmydata.test.test_iputil.ListAddresses.test_list_async_mock_ip_addr allmydata.test.test_iputil.ListAddresses.test_list_async_mock_route allmydata.test.test_iputil.ListenOnUsed.test_random_port +allmydata.test.test_iputil.ListenOnUsed.test_specific_port allmydata.test.test_netstring.Netstring.test_encode allmydata.test.test_netstring.Netstring.test_extra allmydata.test.test_netstring.Netstring.test_nested From 4dae5d867c8dd44d9ceb68b6732030f00b391c6b Mon Sep 17 00:00:00 2001 From: Itamar Turner-Trauring <itamar@itamarst.org> Date: Tue, 28 Jul 2020 11:08:38 -0400 Subject: [PATCH 18/21] Add retry logic, listenOnUnused is potentially flaky. --- setup.py | 1 + src/allmydata/test/test_iputil.py | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/setup.py b/setup.py index 9bb385cb4..a35023b8b 100644 --- a/setup.py +++ b/setup.py @@ -390,6 +390,7 @@ setup(name="tahoe-lafs", # also set in __init__.py "beautifulsoup4", "html5lib", "junitxml", + "tenacity", ] + tor_requires + i2p_requires, "tor": tor_requires, "i2p": i2p_requires, diff --git a/src/allmydata/test/test_iputil.py b/src/allmydata/test/test_iputil.py index 42677af2f..c6caed7f9 100644 --- a/src/allmydata/test/test_iputil.py +++ b/src/allmydata/test/test_iputil.py @@ -17,6 +17,8 @@ import re, errno, subprocess, os, socket from twisted.trial import unittest +from tenacity import retry, stop_after_attempt + from foolscap.api import Tub from allmydata.util import iputil @@ -199,6 +201,7 @@ class ListenOnUsed(unittest.TestCase): self.addCleanup(tub.stopService) return tub + @retry(stop=stop_after_attempt(7)) def test_random_port(self): """A random port is selected if none is given.""" tub = self.create_tub("utils/ListenOnUsed/test_randomport") @@ -215,6 +218,7 @@ class ListenOnUsed(unittest.TestCase): portnum2 = iputil.listenOnUnused(tub2) self.assertNotEqual(portnum, portnum2) + @retry(stop=stop_after_attempt(7)) def test_specific_port(self): """The given port is used.""" tub = self.create_tub("utils/ListenOnUsed/test_givenport") From cc494e3d3978dd79eb67b3397bc8abcb29b7d1c4 Mon Sep 17 00:00:00 2001 From: Itamar Turner-Trauring <itamar@itamarst.org> Date: Tue, 28 Jul 2020 11:11:05 -0400 Subject: [PATCH 19/21] Document that API returns native strings. --- src/allmydata/util/iputil.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/allmydata/util/iputil.py b/src/allmydata/util/iputil.py index 36f4bb79b..890ff98e7 100644 --- a/src/allmydata/util/iputil.py +++ b/src/allmydata/util/iputil.py @@ -102,13 +102,18 @@ except ImportError: increase_rlimits = _increase_rlimits def get_local_addresses_sync(): + """ + Return a list of IPv4 addresses (as dotted-quad native strings) that are + currently configured on this host, sorted in descending order of how likely + we think they are to work. + """ return [native_str(a) for a in _synchronously_find_addresses_via_config()] def get_local_addresses_async(target="198.41.0.4"): # A.ROOT-SERVERS.NET """ Return a Deferred that fires with a list of IPv4 addresses (as dotted-quad - strings) that are currently configured on this host, sorted in descending - order of how likely we think they are to work. + native strings) that are currently configured on this host, sorted in + descending order of how likely we think they are to work. @param target: we want to learn an IP address they could try using to connect to us; The default value is fine, but it might help if you @@ -137,7 +142,7 @@ def get_local_addresses_async(target="198.41.0.4"): # A.ROOT-SERVERS.NET def get_local_ip_for(target): """Find out what our IP address is for use by a given target. - @return: the IP address as a dotted-quad string which could be used by + @return: the IP address as a dotted-quad native string which could be used to connect to us. It might work for them, it might not. If there is no suitable address (perhaps we don't currently have an externally-visible interface), this will return None. @@ -172,7 +177,7 @@ def get_local_ip_for(target): return localip finally: d = port.stopListening() - d.addErrback(log.err) + 3 d.addErrback(log.err) except (socket.error, CannotListenError): # no route to that host localip = None From 8c9c691c02c420253a79393669856d0bea07f4e3 Mon Sep 17 00:00:00 2001 From: Itamar Turner-Trauring <itamar@itamarst.org> Date: Tue, 28 Jul 2020 11:11:27 -0400 Subject: [PATCH 20/21] Fix typo. --- src/allmydata/util/iputil.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/allmydata/util/iputil.py b/src/allmydata/util/iputil.py index 890ff98e7..8754fca53 100644 --- a/src/allmydata/util/iputil.py +++ b/src/allmydata/util/iputil.py @@ -177,7 +177,7 @@ def get_local_ip_for(target): return localip finally: d = port.stopListening() - 3 d.addErrback(log.err) + d.addErrback(log.err) except (socket.error, CannotListenError): # no route to that host localip = None From 199c4f6acd350885e28e30b91216c77d1b739b0e Mon Sep 17 00:00:00 2001 From: Itamar Turner-Trauring <itamar@itamarst.org> Date: Tue, 28 Jul 2020 13:00:44 -0400 Subject: [PATCH 21/21] Try to fix nix. --- nix/tahoe-lafs.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/nix/tahoe-lafs.nix b/nix/tahoe-lafs.nix index 0e941bce1..f2e61d6c2 100644 --- a/nix/tahoe-lafs.nix +++ b/nix/tahoe-lafs.nix @@ -59,6 +59,7 @@ python.pkgs.buildPythonPackage rec { fixtures beautifulsoup4 html5lib + tenacity ]; checkPhase = ''