Hello community,
here is the log from the commit of package python-python-afl for openSUSE:Factory checked in at 2018-07-14 20:25:51
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-python-afl (Old)
and /work/SRC/openSUSE:Factory/.python-python-afl.new (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-python-afl"
Sat Jul 14 20:25:51 2018 rev:3 rq:622789 version:0.7.1
Changes:
--------
--- /work/SRC/openSUSE:Factory/python-python-afl/python-python-afl.changes 2018-07-06 10:45:21.383014700 +0200
+++ /work/SRC/openSUSE:Factory/.python-python-afl.new/python-python-afl.changes 2018-07-14 20:27:41.572567037 +0200
@@ -1,0 +2,13 @@
+Sat Jul 7 08:50:22 UTC 2018 - sebix+novell.com@sebix.at
+
+- update Use-setuptools-and-use-test-command-for-setup.patch
+- update to version 0.7.1:
+ * Reset the SIGCHLD signal handler in the forkserver.
+ Thanks to Kuang-che Wu for the bug report.
+ * Document that sys.stdin rewinding is necessary in persistent mode.
+ * Improve the test suite.
+ + Improve error messages for missing command-line tools.
+ * Explicitly set Cython's Python language level to 2.
+ This might fix build failures with future versions of Cython.
+
+-------------------------------------------------------------------
Old:
----
python-afl-0.7.tar.gz
New:
----
python-afl-0.7.1.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-python-afl.spec ++++++
--- /var/tmp/diff_new_pack.8YQV2J/_old 2018-07-14 20:27:42.140568495 +0200
+++ /var/tmp/diff_new_pack.8YQV2J/_new 2018-07-14 20:27:42.144568505 +0200
@@ -19,7 +19,7 @@
%{?!python_module:%define python_module() python-%{**} python3-%{**}}
%bcond_without test
Name: python-python-afl
-Version: 0.7
+Version: 0.7.1
Release: 0
Summary: American fuzzy lop fork server and instrumentation for pure-Python code
License: MIT
@@ -27,7 +27,7 @@
Url: http://jwilk.net/software/python-afl
Source: https://files.pythonhosted.org/packages/source/p/python-afl/python-afl-%{version}.tar.gz
# PATCH-FIX-OPENSUSE
-Patch0: https://github.com/jwilk/python-afl/compare/0.7...sebix:0.7-fix-setup-tests....
+Patch0: https://github.com/jwilk/python-afl/compare/0.7.1...sebix:0.7.1-fix-setup-te...
BuildRequires: %{python_module Cython >= 0.19}
BuildRequires: %{python_module devel}
BuildRequires: %{python_module setuptools}
++++++ Use-setuptools-and-use-test-command-for-setup.patch ++++++
--- /var/tmp/diff_new_pack.8YQV2J/_old 2018-07-14 20:27:42.156568536 +0200
+++ /var/tmp/diff_new_pack.8YQV2J/_new 2018-07-14 20:27:42.156568536 +0200
@@ -1,14 +1,14 @@
-From 18aa9f38a495deb7ee7c17077980c62835db99d2 Mon Sep 17 00:00:00 2001
+From 2b32ddf54e778a71ddaaab1fdcf35d82814d46f2 Mon Sep 17 00:00:00 2001
From: Sebastian Wagner
Date: Fri, 14 Oct 2016 09:54:20 +0200
Subject: [PATCH] Use setuptools and use test command for setup
---
- setup.py | 68 +++++++++++++---------------------------------------------------
- 1 file changed, 14 insertions(+), 54 deletions(-)
+ setup.py | 69 +++++++++++++---------------------------------------------------
+ 1 file changed, 14 insertions(+), 55 deletions(-)
diff --git a/setup.py b/setup.py
-index d813411..d43b3d3 100644
+index 67cb5f8..d43b3d3 100644
--- a/setup.py
+++ b/setup.py
@@ -32,9 +32,9 @@
@@ -38,7 +38,7 @@
meta = dict(
name='python-afl',
version=get_version(),
-@@ -77,60 +84,13 @@ def get_version():
+@@ -77,61 +84,13 @@ def get_version():
url='http://jwilk.net/software/python-afl',
author='Jakub Wilk',
author_email='jwilk@jwilk.net',
@@ -47,6 +47,7 @@
+ ext_modules = cythonize(extensions),
)
+-min_cython_version = '0.19'
-try:
- import Cython
-except ImportError:
@@ -54,13 +55,13 @@
- # For older versions, we use this hack to trick it into installing Cython:
- if 'setuptools' in sys.modules and sys.argv[1] == 'egg_info':
- distutils.core.setup(
-- install_requires=['Cython>=0.19'],
+- install_requires=['Cython>={v}'.format(v=min_cython_version)],
- # Conceptually, “setup_requires” would make more sense than
- # “install_requires”, but the former is not supported by pip.
- **meta
- )
- sys.exit(0)
-- raise RuntimeError('Cython >= 0.19 is required')
+- raise RuntimeError('Cython >= {v} is required'.format(v=min_cython_version))
-
-try:
- cython_version = Cython.__version__
@@ -69,8 +70,8 @@
- # Oh well. We don't support such old versions anyway.
- cython_version = '0'
-cython_version = distutils.version.LooseVersion(cython_version)
--if cython_version < '0.19':
-- raise RuntimeError('Cython >= 0.19 is required')
+-if cython_version < min_cython_version:
+- raise RuntimeError('Cython >= {v} is required'.format(v=min_cython_version))
-
-import Cython.Build # pylint: disable=wrong-import-position
-
++++++ python-afl-0.7.tar.gz -> python-afl-0.7.1.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-afl-0.7/PKG-INFO new/python-afl-0.7.1/PKG-INFO
--- old/python-afl-0.7/PKG-INFO 2018-04-30 11:04:42.000000000 +0200
+++ new/python-afl-0.7.1/PKG-INFO 2018-06-21 23:22:27.000000000 +0200
@@ -1,6 +1,6 @@
Metadata-Version: 1.1
Name: python-afl
-Version: 0.7
+Version: 0.7.1
Summary: American Fuzzy Lop fork server and instrumentation for pure-Python code
Home-page: http://jwilk.net/software/python-afl
Author: Jakub Wilk
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-afl-0.7/afl.pyx new/python-afl-0.7.1/afl.pyx
--- old/python-afl-0.7/afl.pyx 2018-04-30 10:00:28.000000000 +0200
+++ new/python-afl-0.7.1/afl.pyx 2018-06-20 18:23:34.000000000 +0200
@@ -21,12 +21,13 @@
#cython: autotestdict=False
#cython: c_string_encoding=default
#cython: cdivision=True
+#cython: language_level=2
'''
American Fuzzy Lop fork server and instrumentation for pure-Python code
'''
-__version__ = '0.7'
+__version__ = '0.7.1'
cdef object os, signal, struct, sys, warnings
import os
@@ -43,11 +44,27 @@
from cpython.exc cimport PyErr_SetFromErrno
from libc cimport errno
+from libc.signal cimport SIG_DFL
from libc.stddef cimport size_t
from libc.stdint cimport uint32_t
from libc.stdlib cimport getenv
from libc.string cimport strlen
+cdef extern from 'signal.h':
+ # We could use definitions from posix/signal.pxd,
+ # but these were broken before Cython 0.24.
+ ctypedef struct siginfo_t:
+ pass
+ ctypedef struct sigset_t:
+ pass
+ cdef struct sigaction_t 'sigaction':
+ void sa_handler(int)
+ void sa_sigaction(int, siginfo_t *, void *)
+ sigset_t sa_mask
+ int sa_flags
+ int sigaction(int, const sigaction_t *, sigaction_t *)
+ int sigemptyset(sigset_t *)
+
cdef extern from 'sys/shm.h':
unsigned char *shmat(int shmid, void *shmaddr, int shmflg)
@@ -123,6 +140,16 @@
init_done = True
child_stopped = False
child_pid = 0
+ cdef sigaction_t old_sigchld
+ cdef sigaction_t dfl_sigchld
+ dfl_sigchld.sa_handler = SIG_DFL
+ dfl_sigchld.sa_sigaction = NULL
+ dfl_sigchld.sa_flags = 0
+ sigemptyset(&dfl_sigchld.sa_mask)
+ if use_forkserver:
+ rc = sigaction(signal.SIGCHLD, &dfl_sigchld, &old_sigchld)
+ if rc:
+ PyErr_SetFromErrno(OSError)
while use_forkserver:
[child_killed] = struct.unpack('I', os.read(FORKSRV_FD, 4))
if child_stopped and child_killed:
@@ -142,6 +169,9 @@
child_stopped = os.WIFSTOPPED(status)
os.write(FORKSRV_FD + 1, struct.pack('I', status))
if use_forkserver:
+ rc = sigaction(signal.SIGCHLD, &old_sigchld, NULL)
+ if rc:
+ PyErr_SetFromErrno(OSError)
os.close(FORKSRV_FD)
os.close(FORKSRV_FD + 1)
if except_signal_id != 0:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-afl-0.7/doc/README new/python-afl-0.7.1/doc/README
--- old/python-afl-0.7/doc/README 2018-04-19 12:57:38.000000000 +0200
+++ new/python-afl-0.7.1/doc/README 2018-06-21 23:17:47.000000000 +0200
@@ -42,6 +42,13 @@
You shouldn't call ``afl.init()`` in this case.
+ If you read input from ``sys.stdin``,
+ you must rewind it in every loop iteration:
+
+ .. code:: python
+
+ sys.stdin.seek(0)
+
afl-fuzz ≥ 1.82b is required for this feature.
* Use *py-afl-fuzz* instead of *afl-fuzz*::
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-afl-0.7/doc/changelog new/python-afl-0.7.1/doc/changelog
--- old/python-afl-0.7/doc/changelog 2018-04-30 10:42:20.000000000 +0200
+++ new/python-afl-0.7.1/doc/changelog 2018-06-21 23:17:57.000000000 +0200
@@ -1,3 +1,15 @@
+python-afl (0.7.1) unstable; urgency=low
+
+ * Reset the SIGCHLD signal handler in the forkserver.
+ Thanks to Kuang-che Wu for the bug report.
+ * Document that sys.stdin rewinding is necessary in persistent mode.
+ * Improve the test suite.
+ + Improve error messages for missing command-line tools.
+ * Explicitly set Cython's Python language level to 2.
+ This might fix build failures with future versions of Cython.
+
+ -- Jakub Wilk Thu, 21 Jun 2018 23:17:56 +0200
+
python-afl (0.7) unstable; urgency=low
[ Jakub Wilk ]
@@ -26,6 +38,8 @@
* Improve the test suite.
+ Make the py-afl-cmin test pass when run in a subdirectory of /tmp.
+ * Improve the build system:
+ + Use distutils644 to normalize source tarball permissions etc.
-- Jakub Wilk Fri, 28 Jul 2017 16:43:06 +0200
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-afl-0.7/private/build-and-test new/python-afl-0.7.1/private/build-and-test
--- old/python-afl-0.7/private/build-and-test 2017-04-05 13:25:30.000000000 +0200
+++ new/python-afl-0.7.1/private/build-and-test 2018-06-20 18:23:34.000000000 +0200
@@ -1,6 +1,6 @@
#!/bin/sh
-# Copyright © 2015-2017 Jakub Wilk
+# Copyright © 2015-2018 Jakub Wilk
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the “Software”), to deal
@@ -20,13 +20,14 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
+set -e -u
+
usage()
{
printf 'Usage: %s [--no-build] [pythonX.Y...]\n' "$0"
}
-args=$(getopt -n "$0" -o 'hj:' --long 'help,jobs:,no-build' -- "$@")
-if [ $? -ne 0 ]
+if ! args=$(getopt -n "$0" -o 'hj:' --long 'help,jobs:,no-build' -- "$@")
then
usage >&2
exit 1
@@ -45,7 +46,6 @@
esac
done
-set -e
[ $# = 0 ] && set -- python
[ -z $opt_build ] ||
printf '%s\n' "$@" \
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-afl-0.7/private/check-rst new/python-afl-0.7.1/private/check-rst
--- old/python-afl-0.7/private/check-rst 2017-04-03 23:33:06.000000000 +0200
+++ new/python-afl-0.7.1/private/check-rst 2018-06-20 18:23:34.000000000 +0200
@@ -1,6 +1,6 @@
#!/bin/sh
-# Copyright © 2016-2017 Jakub Wilk
+# Copyright © 2016-2018 Jakub Wilk
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the “Software”), to deal
@@ -20,14 +20,25 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
-here=$(dirname "$0")
+set -e -u
+here=${0%/*}
+here=${here#./}
+root="$here/../"
+root=${root#private/../}
rst2xml=$(command -v rst2xml) \
|| rst2xml=$(command -v rst2xml.py) \
|| { printf 'rst2xml not found\n' >&2; exit 1; }
-options='--input-encoding=UTF-8 --output-encoding=UTF-8 --strict'
+rst2xml=${rst2xml##*/}
+options='--input-encoding=UTF-8 --strict'
if [ $# -eq 0 ]
then
- grep -rwl 'ft[=]rst' "$here/.."
+ print_desc='python setup.py --long-description'
+ echo "(cd ${root:-.} && $print_desc) | $rst2xml $options - /dev/null" >&2
+ (cd "${root:-.}" && $print_desc) | "$rst2xml" $options - /dev/null || exit 1
+fi
+if [ $# -eq 0 ]
+then
+ grep -rwl 'ft=rst' "${root}doc"
else
printf '%s\n' "$@"
fi |
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-afl-0.7/private/run-pylint new/python-afl-0.7.1/private/run-pylint
--- old/python-afl-0.7/private/run-pylint 2018-02-27 23:18:56.000000000 +0100
+++ new/python-afl-0.7.1/private/run-pylint 2018-06-20 18:23:34.000000000 +0200
@@ -33,7 +33,6 @@
# https://github.com/PyCQA/pylint/issues/73
set -- --ignored-modules=distutils "$@"
fi
-set -- --load-plugins=pylint.extensions.check_elif "$@"
log=$(mktemp -t pylint.XXXXXX)
"$PYTHON" -m pylint "$@" > "$log" || [ $? != 1 ]
! grep '^\w:' "$log" \
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-afl-0.7/private/update-version new/python-afl-0.7.1/private/update-version
--- old/python-afl-0.7/private/update-version 2017-06-17 17:53:18.000000000 +0200
+++ new/python-afl-0.7.1/private/update-version 2018-06-20 18:23:34.000000000 +0200
@@ -1,6 +1,6 @@
#!/bin/sh
-version=${1:?"no version number provided"}
+export version=${1:?"no version number provided"}
set -e
set -x
dch -m -v "$version" -u low -c doc/changelog
-sed -i -E -e "s/^(__version__) = '[0-9.]+'$/\1 = '$version'/" afl.pyx
+perl -pi -e 's/^__version__ = '"'"'\K[\w.]+/$ENV{version}/' afl.pyx
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-afl-0.7/setup.py new/python-afl-0.7.1/setup.py
--- old/python-afl-0.7/setup.py 2018-04-26 15:13:29.000000000 +0200
+++ new/python-afl-0.7.1/setup.py 2018-06-20 18:23:34.000000000 +0200
@@ -79,6 +79,7 @@
author_email='jwilk@jwilk.net',
)
+min_cython_version = '0.19'
try:
import Cython
except ImportError:
@@ -86,13 +87,13 @@
# For older versions, we use this hack to trick it into installing Cython:
if 'setuptools' in sys.modules and sys.argv[1] == 'egg_info':
distutils.core.setup(
- install_requires=['Cython>=0.19'],
+ install_requires=['Cython>={v}'.format(v=min_cython_version)],
# Conceptually, “setup_requires” would make more sense than
# “install_requires”, but the former is not supported by pip.
**meta
)
sys.exit(0)
- raise RuntimeError('Cython >= 0.19 is required')
+ raise RuntimeError('Cython >= {v} is required'.format(v=min_cython_version))
try:
cython_version = Cython.__version__
@@ -101,8 +102,8 @@
# Oh well. We don't support such old versions anyway.
cython_version = '0'
cython_version = distutils.version.LooseVersion(cython_version)
-if cython_version < '0.19':
- raise RuntimeError('Cython >= 0.19 is required')
+if cython_version < min_cython_version:
+ raise RuntimeError('Cython >= {v} is required'.format(v=min_cython_version))
import Cython.Build # pylint: disable=wrong-import-position
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-afl-0.7/tests/target.py new/python-afl-0.7.1/tests/target.py
--- old/python-afl-0.7/tests/target.py 2017-04-04 21:58:11.000000000 +0200
+++ new/python-afl-0.7.1/tests/target.py 2018-06-20 18:23:34.000000000 +0200
@@ -1,3 +1,26 @@
+# encoding=UTF-8
+
+# Copyright © 2015-2018 Jakub Wilk
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the “Software”), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+import signal
import sys
import afl
@@ -14,6 +37,7 @@
print('A non-zero value? How quaint!')
if __name__ == '__main__':
+ signal.signal(signal.SIGCHLD, signal.SIG_IGN) # this should have no effect on the forkserver
afl.init()
main()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-afl-0.7/tests/target_persistent.py new/python-afl-0.7.1/tests/target_persistent.py
--- old/python-afl-0.7/tests/target_persistent.py 2018-04-30 10:07:04.000000000 +0200
+++ new/python-afl-0.7.1/tests/target_persistent.py 2018-06-20 18:23:34.000000000 +0200
@@ -1,3 +1,26 @@
+# encoding=UTF-8
+
+# Copyright © 2015-2018 Jakub Wilk
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the “Software”), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+import signal
import sys
import afl
@@ -15,6 +38,7 @@
print('A non-zero value? How quaint!')
if __name__ == '__main__':
+ signal.signal(signal.SIGCHLD, signal.SIG_IGN) # this should have no effect on the forkserver
''.encode('ASCII') # make sure the codec module is loaded before the loop
while afl.loop():
main()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-afl-0.7/tests/test_fuzz.py new/python-afl-0.7.1/tests/test_fuzz.py
--- old/python-afl-0.7/tests/test_fuzz.py 2018-03-12 17:38:22.000000000 +0100
+++ new/python-afl-0.7.1/tests/test_fuzz.py 2018-06-20 18:23:34.000000000 +0200
@@ -137,6 +137,7 @@
def stray_process_cleanup():
# afl-fuzz doesn't always kill the target process:
# https://groups.google.com/d/topic/afl-users/E37s4YDti7o
+ require_commands('ps')
try:
yield
finally:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-afl-0.7/tests/test_hash.py new/python-afl-0.7.1/tests/test_hash.py
--- old/python-afl-0.7/tests/test_hash.py 2017-04-04 22:51:43.000000000 +0200
+++ new/python-afl-0.7.1/tests/test_hash.py 2018-06-20 18:23:34.000000000 +0200
@@ -1,6 +1,6 @@
# encoding=UTF-8
-# Copyright © 2015 Jakub Wilk
+# Copyright © 2015-2017 Jakub Wilk
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the “Software”), to deal
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-afl-0.7/tests/tools.py new/python-afl-0.7.1/tests/tools.py
--- old/python-afl-0.7/tests/tools.py 2018-03-12 17:38:30.000000000 +0100
+++ new/python-afl-0.7.1/tests/tools.py 2018-06-20 18:23:34.000000000 +0200
@@ -143,7 +143,14 @@
if os.access(path, os.X_OK):
break
else:
- raise RuntimeError('{cmd} not found; is PATH set correctly?'.format(cmd=cmd))
+ if cmd == 'ps':
+ cmd = 'ps(1)'
+ reason = 'procps installed'
+ elif cmd.startswith('afl-'):
+ reason = 'AFL installed'
+ else:
+ reason = 'PATH set correctly'
+ raise RuntimeError('{cmd} not found; is {reason}?'.format(cmd=cmd, reason=reason))
def run(cmd, stdin='', xstatus=0):
child = ipc.Popen(