Eliminate dependencies on pywin32, even via Twisted. refs #1274
authordavid-sarah <david-sarah@jacaranda.org>
Thu, 20 Jan 2011 04:32:38 +0000 (20:32 -0800)
committerdavid-sarah <david-sarah@jacaranda.org>
Thu, 20 Jan 2011 04:32:38 +0000 (20:32 -0800)
src/allmydata/_auto_deps.py
src/allmydata/test/test_runner.py
src/allmydata/test/test_system.py
src/allmydata/util/iputil.py

index f77db1c04687ab742a63c7a6ac872c62acc0b29d..b784b8bc515f978b3886d032a5328878f6c3911a 100644 (file)
@@ -59,18 +59,6 @@ if sys.version_info < (2, 5):
     # pysqlite v2.0.5 was shipped in Ubuntu 6.06 LTS "dapper" and Nexenta NCP 1.
     install_requires.append("pysqlite >= 2.0.5")
 
-## The following block is commented-out because there is not currently a pywin32 package which
-## can be easy_install'ed and also which actually makes "import win32api" succeed.
-## See http://sourceforge.net/tracker/index.php?func=detail&aid=1799934&group_id=78018&atid=551954
-## Users have to manually install pywin32 on Windows before installing Tahoe.
-##import platform
-##if platform.system() == "Windows":
-##    # Twisted requires pywin32 if it is going to offer process management functionality, or if
-##    # it is going to offer iocp reactor.  We currently require process management.  It would be
-##    # better if Twisted would declare that it requires pywin32 if it is going to offer process
-##    # management.  That is twisted ticket #3238 -- http://twistedmatrix.com/trac/ticket/3238 .
-##    install_requires.append('pywin32')
-
 if hasattr(sys, 'frozen'): # for py2exe
     install_requires=[]
 del sys # clean up namespace
index f13b8196bf345c279c43943145dbc23cf49aa542..86d31f50300e069598a920d1d59b21b040ddc21e 100644 (file)
@@ -2,7 +2,8 @@
 from twisted.trial import unittest
 
 from twisted.python import usage, runtime
-from twisted.internet import utils
+from twisted.internet import threads
+
 import os.path, re, sys, subprocess
 from cStringIO import StringIO
 from allmydata.util import fileutil, pollmixin
@@ -39,10 +40,8 @@ if sys.platform == "win32":
            bintahoe = alt_bintahoe
 
 
-class SkipMixin:
+class RunBinTahoeMixin:
     def skip_if_cannot_run_bintahoe(self):
-        if "cygwin" in sys.platform.lower():
-            raise unittest.SkipTest("We don't know how to make this test work on cygwin: spawnProcess seems to hang forever. We don't know if 'bin/tahoe start' can be run on cygwin.")
         if not os.path.exists(bintahoe):
             raise unittest.SkipTest("The bin/tahoe script isn't to be found in the expected location (%s), and I don't want to test a 'tahoe' executable that I find somewhere else, in case it isn't the right executable for this version of Tahoe. Perhaps running 'setup.py build' again will help." % (bintahoe,))
 
@@ -52,8 +51,22 @@ class SkipMixin:
             # twistd on windows doesn't daemonize. cygwin should work normally.
             raise unittest.SkipTest("twistd does not fork under windows")
 
+    def run_bintahoe(self, args, stdin=None, python_options=[], env=None):
+        self.skip_if_cannot_run_bintahoe()
+        command = [sys.executable] + python_options + [bintahoe] + args
+        if stdin is None:
+            stdin_stream = None
+        else:
+            stdin_stream = subprocess.PIPE
+
+        def _run():
+            p = subprocess.Popen(command, stdin=stdin_stream, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env)
+            (out, err) = p.communicate(stdin)
+            return (out, err, p.returncode)
+        return threads.deferToThread(_run)
+
 
-class BinTahoe(common_util.SignalMixin, unittest.TestCase, SkipMixin):
+class BinTahoe(common_util.SignalMixin, unittest.TestCase, RunBinTahoeMixin):
     def _check_right_code(self, file_to_check):
         root_to_check = get_root_from_file(file_to_check)
         cwd = os.path.normcase(os.path.realpath("."))
@@ -91,20 +104,19 @@ class BinTahoe(common_util.SignalMixin, unittest.TestCase, SkipMixin):
         self._check_right_code(srcfile)
 
     def test_import_in_repl(self):
-        self.skip_if_cannot_run_bintahoe()
-
-        p = subprocess.Popen([sys.executable, bintahoe, "debug", "repl"],
-                             stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
-        (out, err) = p.communicate("import allmydata; print; print allmydata.__file__")
-
-        self.failUnlessEqual(p.returncode, 0)
-        lines = out.splitlines()
-        self.failUnlessIn('>>>', lines[0], (out, err))
-        self._check_right_code(lines[1])
+        d = self.run_bintahoe(["debug", "repl"],
+                              stdin="import allmydata; print; print allmydata.__file__")
+        def _cb(res):
+            out, err, rc_or_sig = res
+            self.failUnlessEqual(rc_or_sig, 0, str(res))
+            lines = out.splitlines()
+            self.failUnlessIn('>>>', lines[0], str(res))
+            self._check_right_code(lines[1])
+        d.addCallback(_cb)
+        return d
 
     def test_path(self):
-        self.skip_if_cannot_run_bintahoe()
-        d = utils.getProcessOutputAndValue(bintahoe, args=["--version-and-path"], env=os.environ)
+        d = self.run_bintahoe(["--version-and-path"])
         def _cb(res):
             out, err, rc_or_sig = res
             self.failUnlessEqual(rc_or_sig, 0, str(res))
@@ -151,7 +163,7 @@ class BinTahoe(common_util.SignalMixin, unittest.TestCase, SkipMixin):
         except UnicodeEncodeError:
             raise unittest.SkipTest("A non-ASCII argument/output could not be encoded on this platform.")
 
-        d = utils.getProcessOutputAndValue(bintahoe, args=[tricky_arg], env=os.environ)
+        d = self.run_bintahoe([tricky_arg])
         def _cb(res):
             out, err, rc_or_sig = res
             self.failUnlessEqual(rc_or_sig, 1, str(res))
@@ -163,8 +175,7 @@ class BinTahoe(common_util.SignalMixin, unittest.TestCase, SkipMixin):
         self.skip_if_cannot_run_bintahoe()
 
         # -t is a harmless option that warns about tabs.
-        d = utils.getProcessOutputAndValue(sys.executable, args=['-t', bintahoe, '--version'],
-                                           env=os.environ)
+        d = self.run_bintahoe(["--version"], python_options=["-t"])
         def _cb(res):
             out, err, rc_or_sig = res
             self.failUnlessEqual(rc_or_sig, 0, str(res))
@@ -180,7 +191,7 @@ class BinTahoe(common_util.SignalMixin, unittest.TestCase, SkipMixin):
         except pkg_resources.VersionConflict:
             raise unittest.SkipTest("We pass this test only with Twisted >= v9.0.0")
 
-        d = utils.getProcessOutputAndValue(bintahoe, args=["--version"], env=os.environ)
+        d = self.run_bintahoe(["--version"])
         def _cb(res):
             out, err, rc_or_sig = res
             self.failUnlessEqual(rc_or_sig, 0, str(res))
@@ -307,7 +318,7 @@ class CreateNode(unittest.TestCase):
 
 
 class RunNode(common_util.SignalMixin, unittest.TestCase, pollmixin.PollMixin,
-              SkipMixin):
+              RunBinTahoeMixin):
     # exercise "tahoe start", for both introducer, client node, and
     # key-generator, by spawning "tahoe start" as a subprocess. This doesn't
     # get us figleaf-based line-level coverage, but it does a better job of
@@ -333,7 +344,7 @@ class RunNode(common_util.SignalMixin, unittest.TestCase, pollmixin.PollMixin,
         TWISTD_PID_FILE = os.path.join(c1, "twistd.pid")
         INTRODUCER_FURL_FILE = os.path.join(c1, "introducer.furl")
 
-        d = utils.getProcessOutputAndValue(bintahoe, args=["--quiet", "create-introducer", "--basedir", c1], env=os.environ)
+        d = self.run_bintahoe(["--quiet", "create-introducer", "--basedir", c1])
         def _cb(res):
             out, err, rc_or_sig = res
             self.failUnlessEqual(rc_or_sig, 0)
@@ -345,7 +356,7 @@ class RunNode(common_util.SignalMixin, unittest.TestCase, pollmixin.PollMixin,
         d.addCallback(_cb)
 
         def _then_start_the_node(res):
-            return utils.getProcessOutputAndValue(bintahoe, args=["--quiet", "start", c1], env=os.environ)
+            return self.run_bintahoe(["--quiet", "start", c1])
         d.addCallback(_then_start_the_node)
 
         def _cb2(res):
@@ -376,7 +387,7 @@ class RunNode(common_util.SignalMixin, unittest.TestCase, pollmixin.PollMixin,
             self.failUnless(os.path.exists(TWISTD_PID_FILE))
             # rm this so we can detect when the second incarnation is ready
             os.unlink(INTRODUCER_FURL_FILE)
-            return utils.getProcessOutputAndValue(bintahoe, args=["--quiet", "restart", c1], env=os.environ)
+            return self.run_bintahoe(["--quiet", "restart", c1])
         d.addCallback(_started)
 
         def _then(res):
@@ -399,7 +410,7 @@ class RunNode(common_util.SignalMixin, unittest.TestCase, pollmixin.PollMixin,
             open(HOTLINE_FILE, "w").write("")
             self.failUnless(os.path.exists(TWISTD_PID_FILE))
 
-            return utils.getProcessOutputAndValue(bintahoe, args=["--quiet", "stop", c1], env=os.environ)
+            return self.run_bintahoe(["--quiet", "stop", c1])
         d.addCallback(_stop)
 
         def _after_stopping(res):
@@ -436,7 +447,7 @@ class RunNode(common_util.SignalMixin, unittest.TestCase, pollmixin.PollMixin,
         TWISTD_PID_FILE = os.path.join(c1, "twistd.pid")
         PORTNUMFILE = os.path.join(c1, "client.port")
 
-        d = utils.getProcessOutputAndValue(bintahoe, args=["--quiet", "create-client", "--basedir", c1, "--webport", "0"], env=os.environ)
+        d = self.run_bintahoe(["--quiet", "create-client", "--basedir", c1, "--webport", "0"])
         def _cb(res):
             out, err, rc_or_sig = res
             errstr = "cc=%d, OUT: '%s', ERR: '%s'" % (rc_or_sig, out, err)
@@ -451,7 +462,7 @@ class RunNode(common_util.SignalMixin, unittest.TestCase, pollmixin.PollMixin,
         d.addCallback(_cb)
 
         def _start(res):
-            return utils.getProcessOutputAndValue(bintahoe, args=["--quiet", "start", c1], env=os.environ)
+            return self.run_bintahoe(["--quiet", "start", c1])
         d.addCallback(_start)
 
         def _cb2(res):
@@ -485,7 +496,7 @@ class RunNode(common_util.SignalMixin, unittest.TestCase, pollmixin.PollMixin,
         # 'tahoe stop' command takes a while.
         def _stop(res):
             self.failUnless(os.path.exists(TWISTD_PID_FILE), (TWISTD_PID_FILE, os.listdir(os.path.dirname(TWISTD_PID_FILE))))
-            return utils.getProcessOutputAndValue(bintahoe, args=["--quiet", "stop", c1], env=os.environ)
+            return self.run_bintahoe(["--quiet", "stop", c1])
         d.addCallback(_stop)
         return d
 
@@ -497,7 +508,7 @@ class RunNode(common_util.SignalMixin, unittest.TestCase, pollmixin.PollMixin,
         TWISTD_PID_FILE = os.path.join(c1, "twistd.pid")
         PORTNUMFILE = os.path.join(c1, "client.port")
 
-        d = utils.getProcessOutputAndValue(bintahoe, args=["--quiet", "create-node", "--basedir", c1, "--webport", "0"], env=os.environ)
+        d = self.run_bintahoe(["--quiet", "create-node", "--basedir", c1, "--webport", "0"])
         def _cb(res):
             out, err, rc_or_sig = res
             self.failUnlessEqual(rc_or_sig, 0)
@@ -510,7 +521,7 @@ class RunNode(common_util.SignalMixin, unittest.TestCase, pollmixin.PollMixin,
         d.addCallback(_cb)
 
         def _start(res):
-            return utils.getProcessOutputAndValue(bintahoe, args=["--quiet", "start", c1], env=os.environ)
+            return self.run_bintahoe(["--quiet", "start", c1])
         d.addCallback(_start)
 
         def _cb2(res):
@@ -541,7 +552,7 @@ class RunNode(common_util.SignalMixin, unittest.TestCase, pollmixin.PollMixin,
             # rm this so we can detect when the second incarnation is ready
             os.unlink(PORTNUMFILE)
 
-            return utils.getProcessOutputAndValue(bintahoe, args=["--quiet", "restart", c1], env=os.environ)
+            return self.run_bintahoe(["--quiet", "restart", c1])
         d.addCallback(_started)
 
         def _cb3(res):
@@ -564,7 +575,7 @@ class RunNode(common_util.SignalMixin, unittest.TestCase, pollmixin.PollMixin,
         def _stop(res):
             open(HOTLINE_FILE, "w").write("")
             self.failUnless(os.path.exists(TWISTD_PID_FILE), (TWISTD_PID_FILE, os.listdir(os.path.dirname(TWISTD_PID_FILE))))
-            return utils.getProcessOutputAndValue(bintahoe, args=["--quiet", "stop", c1], env=os.environ)
+            return self.run_bintahoe(["--quiet", "stop", c1])
         d.addCallback(_stop)
 
         def _cb4(res):
@@ -592,7 +603,7 @@ class RunNode(common_util.SignalMixin, unittest.TestCase, pollmixin.PollMixin,
         basedir = self.workdir("test_baddir")
         fileutil.make_dirs(basedir)
 
-        d = utils.getProcessOutputAndValue(bintahoe, args=["--quiet", "start", "--basedir", basedir], env=os.environ)
+        d = self.run_bintahoe(["--quiet", "start", "--basedir", basedir])
         def _cb(res):
             out, err, rc_or_sig = res
             self.failUnlessEqual(rc_or_sig, 1)
@@ -600,7 +611,7 @@ class RunNode(common_util.SignalMixin, unittest.TestCase, pollmixin.PollMixin,
         d.addCallback(_cb)
 
         def _then_stop_it(res):
-            return utils.getProcessOutputAndValue(bintahoe, args=["--quiet", "stop", "--basedir", basedir], env=os.environ)
+            return self.run_bintahoe(["--quiet", "stop", "--basedir", basedir])
         d.addCallback(_then_stop_it)
 
         def _cb2(res):
@@ -611,7 +622,7 @@ class RunNode(common_util.SignalMixin, unittest.TestCase, pollmixin.PollMixin,
 
         def _then_start_in_bogus_basedir(res):
             not_a_dir = os.path.join(basedir, "bogus")
-            return utils.getProcessOutputAndValue(bintahoe, args=["--quiet", "start", "--basedir", not_a_dir], env=os.environ)
+            return self.run_bintahoe(["--quiet", "start", "--basedir", not_a_dir])
         d.addCallback(_then_start_in_bogus_basedir)
 
         def _cb3(res):
@@ -628,14 +639,14 @@ class RunNode(common_util.SignalMixin, unittest.TestCase, pollmixin.PollMixin,
         TWISTD_PID_FILE = os.path.join(c1, "twistd.pid")
         KEYGEN_FURL_FILE = os.path.join(c1, "key_generator.furl")
 
-        d = utils.getProcessOutputAndValue(bintahoe, args=["--quiet", "create-key-generator", "--basedir", c1], env=os.environ)
+        d = self.run_bintahoe(["--quiet", "create-key-generator", "--basedir", c1])
         def _cb(res):
             out, err, rc_or_sig = res
             self.failUnlessEqual(rc_or_sig, 0)
         d.addCallback(_cb)
 
         def _start(res):
-            return utils.getProcessOutputAndValue(bintahoe, args=["--quiet", "start", c1], env=os.environ)
+            return self.run_bintahoe(["--quiet", "start", c1])
         d.addCallback(_start)
 
         def _cb2(res):
@@ -663,7 +674,7 @@ class RunNode(common_util.SignalMixin, unittest.TestCase, pollmixin.PollMixin,
             self.failUnless(os.path.exists(TWISTD_PID_FILE))
             # rm this so we can detect when the second incarnation is ready
             os.unlink(KEYGEN_FURL_FILE)
-            return utils.getProcessOutputAndValue(bintahoe, args=["--quiet", "restart", c1], env=os.environ)
+            return self.run_bintahoe(["--quiet", "restart", c1])
         d.addCallback(_started)
 
         def _cb3(res):
@@ -683,7 +694,7 @@ class RunNode(common_util.SignalMixin, unittest.TestCase, pollmixin.PollMixin,
         # 'tahoe stop' command takes a while.
         def _stop(res):
             self.failUnless(os.path.exists(TWISTD_PID_FILE))
-            return utils.getProcessOutputAndValue(bintahoe, args=["--quiet", "stop", c1], env=os.environ)
+            return self.run_bintahoe(["--quiet", "stop", c1])
         d.addCallback(_stop)
 
         def _cb4(res):
index 006d7bdbf8357308ebd076e39425f9e4e3ac0741..ed615981b606188a96f13b677986990b8838fdd8 100644 (file)
@@ -4,7 +4,6 @@ from cStringIO import StringIO
 from twisted.trial import unittest
 from twisted.internet import defer
 from twisted.internet import threads # CLI tests use deferToThread
-from twisted.internet import utils
 
 import allmydata
 from allmydata import uri
@@ -32,7 +31,7 @@ from twisted.web.error import Error
 from allmydata.test.common import SystemTestMixin
 
 # TODO: move these to common or common_util
-from allmydata.test.test_runner import bintahoe, SkipMixin
+from allmydata.test.test_runner import RunBinTahoeMixin
 
 LARGE_DATA = """
 This is some data to publish to the remote grid.., which needs to be large
@@ -52,7 +51,7 @@ class CountingDataUploadable(upload.Data):
                 self.interrupt_after_d.callback(self)
         return upload.Data.read(self, length)
 
-class SystemTest(SystemTestMixin, SkipMixin, unittest.TestCase):
+class SystemTest(SystemTestMixin, RunBinTahoeMixin, unittest.TestCase):
     timeout = 3600 # It takes longer than 960 seconds on Zandr's ARM box.
 
     def test_connections(self):
@@ -1594,23 +1593,9 @@ class SystemTest(SystemTestMixin, SkipMixin, unittest.TestCase):
         d.addCallback(_check_ls_rouri)
 
 
-        d.addCallback(run, "mv", "tahoe-file-stdin", "tahoe-moved-first-time")
+        d.addCallback(run, "mv", "tahoe-file-stdin", "tahoe-moved")
         d.addCallback(run, "ls")
-        d.addCallback(_check_ls, ["tahoe-moved-first-time"], ["tahoe-file-stdin"])
-
-        def _mv_with_http_proxy(ign):
-            env = os.environ
-            env['http_proxy'] = env['HTTP_PROXY'] = "http://127.0.0.0:12345"  # invalid address
-            return self._run_cli_in_subprocess(["mv"] + nodeargs + ["tahoe-moved-first-time", "tahoe-moved"], env=env)
-        d.addCallback(_mv_with_http_proxy)
-
-        def _check_mv_with_http_proxy(res):
-            out, err, rc_or_sig = res
-            self.failUnlessEqual(rc_or_sig, 0, str(res))
-        d.addCallback(_check_mv_with_http_proxy)
-
-        d.addCallback(run, "ls")
-        d.addCallback(_check_ls, ["tahoe-moved"], ["tahoe-moved-firsttime"])
+        d.addCallback(_check_ls, ["tahoe-moved"], ["tahoe-file-stdin"])
 
         d.addCallback(run, "ln", "tahoe-moved", "newlink")
         d.addCallback(run, "ls")
@@ -1753,6 +1738,52 @@ class SystemTest(SystemTestMixin, SkipMixin, unittest.TestCase):
 
         return d
 
+    def test_filesystem_with_cli_in_subprocess(self):
+        # We do this in a separate test so that test_filesystem doesn't skip if we can't run bin/tahoe.
+
+        self.basedir = "system/SystemTest/test_filesystem_with_cli_in_subprocess"
+        d = self.set_up_nodes()
+        def _new_happy_semantics(ign):
+            for c in self.clients:
+                c.DEFAULT_ENCODING_PARAMETERS['happy'] = 1
+        d.addCallback(_new_happy_semantics)
+
+        def _run_in_subprocess(ignored, verb, *args, **kwargs):
+            stdin = kwargs.get("stdin")
+            env = kwargs.get("env")
+            newargs = [verb, "--node-directory", self.getdir("client0")] + list(args)
+            return self.run_bintahoe(newargs, stdin=stdin, env=env)
+
+        def _check_succeeded(res, check_stderr=True):
+            out, err, rc_or_sig = res
+            self.failUnlessEqual(rc_or_sig, 0, str(res))
+            if check_stderr:
+                self.failUnlessEqual(err, "")
+
+        d.addCallback(_run_in_subprocess, "create-alias", "newalias")
+        d.addCallback(_check_succeeded)
+
+        STDIN_DATA = "This is the file to upload from stdin."
+        d.addCallback(_run_in_subprocess, "put", "-", "newalias:tahoe-file", stdin=STDIN_DATA)
+        d.addCallback(_check_succeeded, check_stderr=False)
+
+        def _mv_with_http_proxy(ign):
+            env = os.environ
+            env['http_proxy'] = env['HTTP_PROXY'] = "http://127.0.0.0:12345"  # invalid address
+            return _run_in_subprocess(None, "mv", "newalias:tahoe-file", "newalias:tahoe-moved", env=env)
+        d.addCallback(_mv_with_http_proxy)
+        d.addCallback(_check_succeeded)
+
+        d.addCallback(_run_in_subprocess, "ls", "newalias:")
+        def _check_ls(res):
+            out, err, rc_or_sig = res
+            self.failUnlessEqual(rc_or_sig, 0, str(res))
+            self.failUnlessEqual(err, "", str(res))
+            self.failUnlessIn("tahoe-moved", out)
+            self.failIfIn("tahoe-file", out)
+        d.addCallback(_check_ls)
+        return d
+
     def test_debug_trial(self):
         def _check_for_line(lines, result, test):
             for l in lines:
@@ -1765,8 +1796,8 @@ class SystemTest(SystemTestMixin, SkipMixin, unittest.TestCase):
             self.failUnlessIn(outcome, out, "output (prefixed with '##') does not contain %r:\n## %s"
                                             % (outcome, "\n## ".join(lines)))
 
-        d = self._run_cli_in_subprocess(['debug', 'trial', '--reporter=verbose',
-                                         'allmydata.test.trialtest'])
+        d = self.run_bintahoe(['debug', 'trial', '--reporter=verbose',
+                               'allmydata.test.trialtest'])
         def _check_failure( (out, err, rc) ):
             self.failUnlessEqual(rc, 1)
             lines = out.split('\n')
@@ -1779,8 +1810,8 @@ class SystemTest(SystemTestMixin, SkipMixin, unittest.TestCase):
         d.addCallback(_check_failure)
 
         # the --quiet argument regression-tests a problem in finding which arguments to pass to trial
-        d.addCallback(lambda ign: self._run_cli_in_subprocess(['--quiet', 'debug', 'trial', '--reporter=verbose',
-                                                               'allmydata.test.trialtest.Success']))
+        d.addCallback(lambda ign: self.run_bintahoe(['--quiet', 'debug', 'trial', '--reporter=verbose',
+                                                     'allmydata.test.trialtest.Success']))
         def _check_success( (out, err, rc) ):
             self.failUnlessEqual(rc, 0)
             lines = out.split('\n')
@@ -1801,15 +1832,6 @@ class SystemTest(SystemTestMixin, SkipMixin, unittest.TestCase):
         d.addCallback(_done)
         return d
 
-    def _run_cli_in_subprocess(self, argv, env=None):
-        self.skip_if_cannot_run_bintahoe()
-
-        if env is None:
-            env = os.environ
-        d = utils.getProcessOutputAndValue(sys.executable, args=[bintahoe] + argv,
-                                           env=env)
-        return d
-
     def _test_checker(self, res):
         ut = upload.Data("too big to be literal" * 200, convergence=None)
         d = self._personal_node.add_file(u"big file", ut)
index 0d8b9a4e8e380e8e08e18a3b356ddfd633fa9bad..efec11c9a3cc3d3df2b64033ff10609fabbf63c2 100644 (file)
@@ -1,16 +1,12 @@
 # from the Python Standard Library
-import os, re, socket, sys
+import os, re, socket, sys, subprocess
 
 # from Twisted
-from twisted.internet import defer
-from twisted.internet import reactor
+from twisted.internet import defer, threads, reactor
 from twisted.internet.protocol import DatagramProtocol
-from twisted.internet.utils import getProcessOutput
 from twisted.python.procutils import which
 from twisted.python import log
 
-from allmydata.util import observer
-
 try:
     import resource
     def increase_rlimits():
@@ -193,39 +189,6 @@ _irix_path = '/usr/etc/ifconfig'
 # Solaris 2.x
 _sunos_path = '/usr/sbin/ifconfig'
 
-class SequentialTrier(object):
-    """ I hold a list of executables to try and try each one in turn
-    until one gives me a list of IP addresses."""
-
-    def __init__(self, exebasename, args, regex):
-        assert not os.path.isabs(exebasename)
-        self.exes_left_to_try = which(exebasename)
-        self.exes_left_to_try.reverse()
-        self.args = args
-        self.regex = regex
-        self.o = observer.OneShotObserverList()
-        self._try_next()
-
-    def _try_next(self):
-        if not self.exes_left_to_try:
-            self.o.fire(None)
-        else:
-            exe = self.exes_left_to_try.pop()
-            d2 = _query(exe, self.args, self.regex)
-
-            def cb(res):
-                if res:
-                    self.o.fire(res)
-                else:
-                    self._try_next()
-
-            def eb(why):
-                self._try_next()
-
-            d2.addCallbacks(cb, eb)
-
-    def when_tried(self):
-        return self.o.when_fired()
 
 # k: platform string as provided in the value of _platform_map
 # v: tuple of (path_to_tool, args, regex,)
@@ -237,7 +200,11 @@ _tool_map = {
     "irix": (_irix_path, _netbsd_args, _netbsd_re,),
     "sunos": (_sunos_path, _netbsd_args, _netbsd_re,),
     }
+
 def _find_addresses_via_config():
+    return threads.deferToThread(_synchronously_find_addresses_via_config)
+
+def _synchronously_find_addresses_via_config():
     # originally by Greg Smith, hacked by Zooko to conform to Brian's API
 
     platform = _platform_map.get(sys.platform)
@@ -254,23 +221,30 @@ def _find_addresses_via_config():
     if os.path.isabs(pathtotool):
         return _query(pathtotool, args, regex)
     else:
-        return SequentialTrier(pathtotool, args, regex).when_tried()
+        exes_to_try = which(pathtotool)
+        for exe in exes_to_try:
+            try:
+                addresses = _query(exe, args, regex)
+            except Exception:
+                addresses = []
+            if addresses:
+                return addresses
+        return []
 
 def _query(path, args, regex):
-    d = getProcessOutput(path, args)
-    def _parse(output):
-        addresses = []
-        outputsplit = output.split('\n')
-        for outline in outputsplit:
-            m = regex.match(outline)
-            if m:
-                addr = m.groupdict()['address']
-                if addr not in addresses:
-                    addresses.append(addr)
+    p = subprocess.Popen([path] + list(args), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+    (output, err) = p.communicate()
 
-        return addresses
-    d.addCallback(_parse)
-    return d
+    addresses = []
+    outputsplit = output.split('\n')
+    for outline in outputsplit:
+        m = regex.match(outline)
+        if m:
+            addr = m.groupdict()['address']
+            if addr not in addresses:
+                addresses.append(addr)
+
+    return addresses
 
 def _cygwin_hack_find_addresses(target):
     addresses = []