summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGiampaolo Rodola <g.rodola@gmail.com>2020-04-30 12:20:05 +0200
committerGiampaolo Rodola <g.rodola@gmail.com>2020-04-30 12:20:05 +0200
commitc5095d7366d806583df565b364b71cf2aba59fed (patch)
treeea9da05d947a7824e1bbac02a0a222cdeba43a31
parent93c1138ca81b8bf5edd250fc470e1a5770dbdcdd (diff)
downloadpsutil-c5095d7366d806583df565b364b71cf2aba59fed.tar.gz
See: #1709: allow per-test parallelization
Refactor test runner.py with a saner unittest-based class hierarchy so that --parallel args affects all test suites (all, by-name, failed). Also change Makefile which now can be used like this: make test-process ARGS=--parallel
-rwxr-xr-x.ci/travis/run.sh2
-rw-r--r--.cirrus.yml4
-rw-r--r--HISTORY.rst2
-rw-r--r--Makefile49
-rwxr-xr-xpsutil/tests/runner.py238
-rwxr-xr-xpsutil/tests/test_osx.py40
-rwxr-xr-xpsutil/tests/test_testutils.py11
-rwxr-xr-xscripts/internal/winmake.py11
8 files changed, 187 insertions, 170 deletions
diff --git a/.ci/travis/run.sh b/.ci/travis/run.sh
index 562564b0..879e78a6 100755
--- a/.ci/travis/run.sh
+++ b/.ci/travis/run.sh
@@ -22,7 +22,7 @@ python setup.py develop
if [[ $PYVER == '2.7' ]] && [[ "$(uname -s)" != 'Darwin' ]]; then
PSUTIL_TESTING=1 python -Wa -m coverage run psutil/tests/runner.py
else
- PSUTIL_TESTING=1 python -Wa psutil/tests/runner.py --parallel
+ PSUTIL_TESTING=1 python -Wa psutil/tests/runner.py
fi
if [ "$PYVER" == "2.7" ] || [ "$PYVER" == "3.6" ]; then
diff --git a/.cirrus.yml b/.cirrus.yml
index 129644c5..4b8676bc 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -9,7 +9,7 @@ freebsd_13_py3_task:
- python3 -m pip install --user setuptools concurrencytest
- make clean
- make install
- - make test-parallel
+ - make test
- make test-memleaks
- make print-access-denied
- make print-api-speed
@@ -25,7 +25,7 @@ freebsd_11_py2_task:
- python2.7 -m pip install --user setuptools ipaddress mock concurrencytest
- make clean
- make install
- - make test-parallel
+ - make test
- make test-memleaks
- make print-access-denied
- make print-api-speed
diff --git a/HISTORY.rst b/HISTORY.rst
index 7b992a8c..c375934c 100644
--- a/HISTORY.rst
+++ b/HISTORY.rst
@@ -10,6 +10,8 @@ XXXX-XX-XX
- 1729_: parallel tests on UNIX (make test-parallel).
- 1736_: psutil.Popen now inherits from subprocess.Popen instead of
psutil.Process. Also, wait(timeout=...) parameter is backported to Python 2.7.
+- 1741_: "make build/install" is now run in parallel and it's about 15% faster
+ on UNIX.
**Bug fixes**
diff --git a/Makefile b/Makefile
index 1fad22f5..f6e8ba70 100644
--- a/Makefile
+++ b/Makefile
@@ -2,10 +2,12 @@
# To use a specific Python version run: "make install PYTHON=python3.3"
# You can set the variables below from the command line.
+# Configurable.
PYTHON = python3
-TSCRIPT = psutil/tests/runner.py
ARGS =
-# List of nice-to-have dev libs.
+TSCRIPT = psutil/tests/runner.py
+
+# Internal.
DEPS = \
argparse \
check-manifest \
@@ -26,6 +28,12 @@ PY2_DEPS = \
unittest2
DEPS += `$(PYTHON) -c \
"import sys; print('$(PY2_DEPS)' if sys.version_info[0] == 2 else '')"`
+# "python3 setup.py build" can be parallelized on Python >= 3.6.
+BUILD_OPTS = `$(PYTHON) -c \
+ "import sys, os; \
+ py36 = sys.version_info[:2] >= (3, 6); \
+ cpus = os.cpu_count() or 1 if py36 else 1; \
+ print('--parallel %s' % cpus if cpus > 1 else '')"`
# In not in a virtualenv, add --user options for install commands.
INSTALL_OPTS = `$(PYTHON) -c \
"import sys; print('' if hasattr(sys, 'real_prefix') else '--user')"`
@@ -62,14 +70,13 @@ clean: ## Remove all build files.
_:
-build: _ ## Compile without installing.
+build: _ ## Compile (in parallel) without installing.
# make sure setuptools is installed (needed for 'develop' / edit mode)
$(PYTHON) -c "import setuptools"
- PYTHONWARNINGS=all $(PYTHON) setup.py build
- @# copies compiled *.so files in ./psutil directory in order to allow
- @# "import psutil" when using the interactive interpreter from within
- @# this directory.
- PYTHONWARNINGS=all $(PYTHON) setup.py build_ext -i
+ @# "build_ext -i" copies compiled *.so files in ./psutil directory in order
+ @# to allow "import psutil" when using the interactive interpreter from
+ @# within this directory.
+ PYTHONWARNINGS=all $(PYTHON) setup.py build_ext -i $(BUILD_OPTS)
$(PYTHON) -c "import psutil" # make sure it actually worked
install: ## Install this package as current user in "edit" mode.
@@ -111,51 +118,51 @@ setup-dev-env: ## Install GIT hooks, pip, test deps (also upgrades them).
test: ## Run all tests.
${MAKE} install
- $(TEST_PREFIX) $(PYTHON) $(TSCRIPT)
+ $(TEST_PREFIX) $(PYTHON) $(TSCRIPT) $(ARGS)
test-parallel: ## Run all tests in parallel.
${MAKE} install
- $(TEST_PREFIX) $(PYTHON) $(TSCRIPT) --parallel
+ $(TEST_PREFIX) $(PYTHON) $(TSCRIPT) $(ARGS) --parallel
test-process: ## Run process-related API tests.
${MAKE} install
- $(TEST_PREFIX) $(PYTHON) psutil/tests/test_process.py
+ $(TEST_PREFIX) $(PYTHON) $(TSCRIPT) $(ARGS) psutil/tests/test_process.py
test-system: ## Run system-related API tests.
${MAKE} install
- $(TEST_PREFIX) $(PYTHON) psutil/tests/test_system.py
+ $(TEST_PREFIX) $(PYTHON) $(TSCRIPT) $(ARGS) psutil/tests/test_system.py
test-misc: ## Run miscellaneous tests.
${MAKE} install
- $(TEST_PREFIX) $(PYTHON) psutil/tests/test_misc.py
+ $(TEST_PREFIX) $(PYTHON) $(TSCRIPT) $(ARGS) psutil/tests/test_misc.py
test-testutils: ## Run test utils tests.
${MAKE} install
- $(TEST_PREFIX) $(PYTHON) psutil/tests/test_testutils.py
+ $(TEST_PREFIX) $(PYTHON) $(TSCRIPT) $(ARGS) psutil/tests/test_testutils.py
test-unicode: ## Test APIs dealing with strings.
${MAKE} install
- $(TEST_PREFIX) $(PYTHON) psutil/tests/test_unicode.py
+ $(TEST_PREFIX) $(PYTHON) $(TSCRIPT) $(ARGS) psutil/tests/test_unicode.py
test-contracts: ## APIs sanity tests.
${MAKE} install
- $(TEST_PREFIX) $(PYTHON) psutil/tests/test_contracts.py
+ $(TEST_PREFIX) $(PYTHON) $(TSCRIPT) $(ARGS) psutil/tests/test_contracts.py
test-connections: ## Test net_connections() and Process.connections().
${MAKE} install
- $(TEST_PREFIX) $(PYTHON) psutil/tests/test_connections.py
+ $(TEST_PREFIX) $(PYTHON) $(TSCRIPT) $(ARGS) psutil/tests/test_connections.py
test-posix: ## POSIX specific tests.
${MAKE} install
- $(TEST_PREFIX) $(PYTHON) psutil/tests/test_posix.py
+ $(TEST_PREFIX) $(PYTHON) $(TSCRIPT) $(ARGS) psutil/tests/test_posix.py
test-platform: ## Run specific platform tests only.
${MAKE} install
- $(TEST_PREFIX) $(PYTHON) psutil/tests/test_`$(PYTHON) -c 'import psutil; print([x.lower() for x in ("LINUX", "BSD", "OSX", "SUNOS", "WINDOWS", "AIX") if getattr(psutil, x)][0])'`.py
+ $(TEST_PREFIX) $(PYTHON) $(TSCRIPT) $(ARGS) psutil/tests/test_`$(PYTHON) -c 'import psutil; print([x.lower() for x in ("LINUX", "BSD", "OSX", "SUNOS", "WINDOWS", "AIX") if getattr(psutil, x)][0])'`.py
test-memleaks: ## Memory leak tests.
${MAKE} install
- $(TEST_PREFIX) $(PYTHON) psutil/tests/test_memory_leaks.py
+ $(TEST_PREFIX) $(PYTHON) $(TSCRIPT) $(ARGS) psutil/tests/test_memory_leaks.py
test-by-name: ## e.g. make test-by-name ARGS=psutil.tests.test_system.TestSystemAPIs
${MAKE} install
@@ -163,7 +170,7 @@ test-by-name: ## e.g. make test-by-name ARGS=psutil.tests.test_system.TestSyste
test-failed: ## Re-run tests which failed on last run
${MAKE} install
- $(TEST_PREFIX) $(PYTHON) $(TSCRIPT) --last-failed
+ $(TEST_PREFIX) $(PYTHON) $(TSCRIPT) $(ARGS) --last-failed
test-coverage: ## Run test coverage.
${MAKE} install
diff --git a/psutil/tests/runner.py b/psutil/tests/runner.py
index 8e4c872a..ef135c8d 100755
--- a/psutil/tests/runner.py
+++ b/psutil/tests/runner.py
@@ -10,6 +10,14 @@ Unit test runner, providing new features on top of unittest module:
- parallel run (UNIX only)
- print failures/tracebacks on CTRL+C
- re-run failed tests only (make test-failed)
+
+Invocation examples:
+- make test
+- make test-failed
+
+Parallel:
+- make test-parallel
+- make test-process ARGS=--parallel
"""
from __future__ import print_function
@@ -20,9 +28,6 @@ import sys
import textwrap
import time
import unittest
-from unittest import TestResult
-from unittest import TextTestResult
-from unittest import TextTestRunner
try:
import ctypes
except ImportError:
@@ -37,6 +42,7 @@ import psutil
from psutil._common import hilite
from psutil._common import print_color
from psutil._common import term_supports_colors
+from psutil._compat import super
from psutil.tests import APPVEYOR
from psutil.tests import import_module_by_path
from psutil.tests import reap_children
@@ -44,61 +50,15 @@ from psutil.tests import safe_rmpath
from psutil.tests import TOX
-HERE = os.path.abspath(os.path.dirname(__file__))
VERBOSITY = 1 if TOX else 2
FAILED_TESTS_FNAME = '.failed-tests.txt'
NWORKERS = psutil.cpu_count() or 1
+HERE = os.path.abspath(os.path.dirname(__file__))
loadTestsFromTestCase = unittest.defaultTestLoader.loadTestsFromTestCase
-# =====================================================================
-# --- unittest subclasses
-# =====================================================================
-
-
-class ColouredResult(TextTestResult):
-
- def _print_color(self, s, color, bold=False):
- print_color(s, color, bold=bold, file=self.stream)
-
- def addSuccess(self, test):
- TestResult.addSuccess(self, test)
- self._print_color("OK", "green")
-
- def addError(self, test, err):
- TestResult.addError(self, test, err)
- self._print_color("ERROR", "red", bold=True)
-
- def addFailure(self, test, err):
- TestResult.addFailure(self, test, err)
- self._print_color("FAIL", "red")
-
- def addSkip(self, test, reason):
- TestResult.addSkip(self, test, reason)
- self._print_color("skipped: %s" % reason.strip(), "brown")
-
- def printErrorList(self, flavour, errors):
- flavour = hilite(flavour, "red", bold=flavour == 'ERROR')
- TextTestResult.printErrorList(self, flavour, errors)
-
-
-class ColouredTextRunner(TextTestRunner):
- resultclass = ColouredResult
-
- def _makeResult(self):
- # Store result instance so that it can be accessed on
- # KeyboardInterrupt.
- self.result = TextTestRunner._makeResult(self)
- return self.result
-
-
-# =====================================================================
-# --- public API
-# =====================================================================
-
-
-class SuiteLoader:
+class TestLoader:
testdir = HERE
skip_files = ['test_memory_leaks.py']
@@ -130,17 +90,6 @@ class SuiteLoader:
suite.addTest(test)
return suite
- def parallel(self):
- serial = unittest.TestSuite()
- parallel = unittest.TestSuite()
- for obj in self._iter_testmod_classes():
- test = loadTestsFromTestCase(obj)
- if getattr(obj, '_serialrun', False):
- serial.addTest(test)
- else:
- parallel.addTest(test)
- return (serial, parallel)
-
def last_failed(self):
# ...from previously failed test run
suite = unittest.TestSuite()
@@ -154,22 +103,57 @@ class SuiteLoader:
return suite
def from_name(self, name):
- suite = unittest.TestSuite()
if name.endswith('.py'):
name = os.path.splitext(os.path.basename(name))[0]
- suite.addTest(unittest.defaultTestLoader.loadTestsFromName(name))
- return suite
+ return unittest.defaultTestLoader.loadTestsFromName(name)
+
+
+class ColouredResult(unittest.TextTestResult):
+
+ def _print_color(self, s, color, bold=False):
+ print_color(s, color, bold=bold, file=self.stream)
+
+ def addSuccess(self, test):
+ unittest.TestResult.addSuccess(self, test)
+ self._print_color("OK", "green")
+
+ def addError(self, test, err):
+ unittest.TestResult.addError(self, test, err)
+ self._print_color("ERROR", "red", bold=True)
+
+ def addFailure(self, test, err):
+ unittest.TestResult.addFailure(self, test, err)
+ self._print_color("FAIL", "red")
+
+ def addSkip(self, test, reason):
+ unittest.TestResult.addSkip(self, test, reason)
+ self._print_color("skipped: %s" % reason.strip(), "brown")
+
+ def printErrorList(self, flavour, errors):
+ flavour = hilite(flavour, "red", bold=flavour == 'ERROR')
+ super().printErrorList(flavour, errors)
-class Runner:
+class ColouredTextRunner(unittest.TextTestRunner):
+ """
+ A coloured text runner which also prints failed tests on KeyboardInterrupt
+ and save failed tests in a file so that they can be re-run.
+ """
+
+ if term_supports_colors() and not APPVEYOR:
+ resultclass = ColouredResult
+ else:
+ resultclass = unittest.TextTestResult
- def __init__(self):
- self.loader = SuiteLoader()
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
self.failed_tnames = set()
- if term_supports_colors() and not APPVEYOR:
- self.runner = ColouredTextRunner(verbosity=VERBOSITY)
- else:
- self.runner = TextTestRunner(verbosity=VERBOSITY)
+
+ def _makeResult(self):
+ # Store result instance so that it can be accessed on
+ # KeyboardInterrupt.
+ self.result = super()._makeResult()
+ return self.result
def _write_last_failed(self):
if self.failed_tnames:
@@ -185,7 +169,7 @@ class Runner:
def _run(self, suite):
try:
- result = self.runner.run(suite)
+ result = super().run(suite)
except (KeyboardInterrupt, SystemExit):
result = self.runner.result
result.printErrors()
@@ -194,32 +178,25 @@ class Runner:
self._save_result(result)
return result
- def _finalize(self, success):
+ def _exit(self, success):
if success:
+ print_color("SUCCESS", "green", bold=True)
safe_rmpath(FAILED_TESTS_FNAME)
+ sys.exit(0)
else:
+ print_color("FAILED", "red", bold=True)
self._write_last_failed()
- print_color("FAILED", "red")
sys.exit(1)
- def run(self, suite=None):
- """Run tests serially (1 process)."""
- if suite is None:
- suite = self.loader.all()
+ def run(self, suite):
result = self._run(suite)
- self._finalize(result.wasSuccessful())
+ self._exit(result.wasSuccessful())
- def run_last_failed(self):
- """Run tests which failed in the last run."""
- self.run(self.loader.last_failed())
- def run_from_name(self, name):
- """Run test by name, e.g.:
- "test_linux.TestSystemCPUStats.test_ctx_switches"
- """
- self.run(self.loader.from_name(name))
+class ParallelRunner(ColouredTextRunner):
- def _parallelize_suite(self, suite):
+ @staticmethod
+ def _parallelize(suite):
def fdopen(*args, **kwds):
stream = orig_fdopen(*args, **kwds)
atexit.register(stream.close)
@@ -232,18 +209,33 @@ class Runner:
forker = concurrencytest.fork_for_tests(NWORKERS)
return concurrencytest.ConcurrentTestSuite(suite, forker)
- def run_parallel(self):
- """Run tests in parallel."""
- ser_suite, par_suite = self.loader.parallel()
- par_suite = self._parallelize_suite(par_suite)
+ @staticmethod
+ def _split_suite(suite):
+ serial = unittest.TestSuite()
+ parallel = unittest.TestSuite()
+ for test in suite._tests:
+ if test.countTestCases() == 0:
+ continue
+ test_class = test._tests[0].__class__
+ if getattr(test_class, '_serialrun', False):
+ serial.addTest(loadTestsFromTestCase(test_class))
+ else:
+ parallel.addTest(loadTestsFromTestCase(test_class))
+ return (serial, parallel)
+
+ def run(self, suite):
+ ser_suite, par_suite = self._split_suite(suite)
+ par_suite = self._parallelize(par_suite)
# run parallel
- print("starting parallel tests using %s workers" % NWORKERS)
+ print_color("starting parallel tests using %s workers" % NWORKERS,
+ "green", bold=True)
t = time.time()
par = self._run(par_suite)
par_elapsed = time.time() - t
- # cleanup workers and test subprocesses
+ # At this point we should have N zombies (the workers), which
+ # will disappear with wait().
orphans = psutil.Process().children()
gone, alive = psutil.wait_procs(orphans, timeout=1)
if alive:
@@ -256,7 +248,7 @@ class Runner:
ser_elapsed = time.time() - t
# print
- if not par.wasSuccessful():
+ if not par.wasSuccessful() and ser_suite.countTestCases() > 0:
par.printErrors() # print them again at the bottom
par_fails, par_errs, par_skips = map(len, (par.failures,
par.errors,
@@ -264,7 +256,6 @@ class Runner:
ser_fails, ser_errs, ser_skips = map(len, (ser.failures,
ser.errors,
ser.skipped))
- print("-" * 70)
print(textwrap.dedent("""
+----------+----------+----------+----------+----------+----------+
| | total | failures | errors | skipped | time |
@@ -278,14 +269,33 @@ class Runner:
print("Ran %s tests in %.3fs using %s workers" % (
par.testsRun + ser.testsRun, par_elapsed + ser_elapsed, NWORKERS))
ok = par.wasSuccessful() and ser.wasSuccessful()
- self._finalize(ok)
+ self._exit(ok)
+
+
+def get_runner(parallel=False):
+ def warn(msg):
+ print_color(msg + " Running serial tests instead.",
+ "red", file=sys.stderr)
+ if parallel:
+ if psutil.WINDOWS:
+ warn("Can't run parallel tests on Windows.")
+ elif concurrencytest is None:
+ warn("concurrencytest module is not installed.")
+ elif NWORKERS == 1:
+ warn("Only 1 CPU available.")
+ else:
+ return ParallelRunner(verbosity=VERBOSITY)
+ return ColouredTextRunner(verbosity=VERBOSITY)
-runner = Runner()
-run_from_name = runner.run_from_name
+# Used by test_*,py modules.
+def run_from_name(name):
+ suite = TestLoader().from_name(name)
+ runner = get_runner()
+ runner.run(suite)
-def _setup():
+def setup():
if 'PSUTIL_TESTING' not in os.environ:
# This won't work on Windows but set_testing() below will do it.
os.environ['PSUTIL_TESTING'] = '1'
@@ -293,7 +303,7 @@ def _setup():
def main():
- _setup()
+ setup()
usage = "python3 -m psutil.tests [opts] [test-name]"
parser = optparse.OptionParser(usage=usage, description="run unit tests")
parser.add_option("--last-failed",
@@ -307,26 +317,22 @@ def main():
if not opts.last_failed:
safe_rmpath(FAILED_TESTS_FNAME)
- # test-by-name
+ # loader
+ loader = TestLoader()
if args:
if len(args) > 1:
parser.print_usage()
return sys.exit(1)
- return runner.run_from_name(args[0])
+ else:
+ suite = loader.from_name(args[0])
elif opts.last_failed:
- runner.run_last_failed()
- elif not opts.parallel:
- runner.run()
- # parallel
- elif concurrencytest is None:
- print_color("concurrencytest module is not installed; "
- "running serial tests instead", "red")
- runner.run()
- elif NWORKERS == 1:
- print_color("only 1 CPU; running serial tests instead", "red")
- runner.run()
+ suite = loader.last_failed()
else:
- runner.run_parallel()
+ suite = loader.all()
+
+ # runner
+ runner = get_runner(opts.parallel)
+ runner.run(suite)
if __name__ == '__main__':
diff --git a/psutil/tests/test_osx.py b/psutil/tests/test_osx.py
index 4fa8d0af..4df6a884 100755
--- a/psutil/tests/test_osx.py
+++ b/psutil/tests/test_osx.py
@@ -100,63 +100,63 @@ class TestProcess(unittest.TestCase):
time.strftime("%Y", time.localtime(start_psutil)))
+# TODO: probably needs removal (duplicate)
@unittest.skipIf(not MACOS, "MACOS only")
class TestZombieProcessAPIs(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.parent, cls.zombie = create_zombie_proc()
- cls.p = psutil.Process(cls.zombie.pid)
@classmethod
def tearDownClass(cls):
+ terminate(cls.parent)
terminate(cls.zombie)
- terminate(cls.parent) # executed first
def test_pidtask_info(self):
- self.assertEqual(self.p.status(), psutil.STATUS_ZOMBIE)
- self.p.ppid()
- self.p.uids()
- self.p.gids()
- self.p.terminal()
- self.p.create_time()
+ self.assertEqual(self.zombie.status(), psutil.STATUS_ZOMBIE)
+ self.zombie.ppid()
+ self.zombie.uids()
+ self.zombie.gids()
+ self.zombie.terminal()
+ self.zombie.create_time()
def test_exe(self):
- self.assertRaises(psutil.ZombieProcess, self.p.exe)
+ self.assertRaises(psutil.ZombieProcess, self.zombie.exe)
def test_cmdline(self):
- self.assertRaises(psutil.ZombieProcess, self.p.cmdline)
+ self.assertRaises(psutil.ZombieProcess, self.zombie.cmdline)
def test_environ(self):
- self.assertRaises(psutil.ZombieProcess, self.p.environ)
+ self.assertRaises(psutil.ZombieProcess, self.zombie.environ)
def test_cwd(self):
- self.assertRaises(psutil.ZombieProcess, self.p.cwd)
+ self.assertRaises(psutil.ZombieProcess, self.zombie.cwd)
def test_memory_full_info(self):
- self.assertRaises(psutil.ZombieProcess, self.p.memory_full_info)
+ self.assertRaises(psutil.ZombieProcess, self.zombie.memory_full_info)
def test_cpu_times(self):
- self.assertRaises(psutil.ZombieProcess, self.p.cpu_times)
+ self.assertRaises(psutil.ZombieProcess, self.zombie.cpu_times)
def test_num_ctx_switches(self):
- self.assertRaises(psutil.ZombieProcess, self.p.num_ctx_switches)
+ self.assertRaises(psutil.ZombieProcess, self.zombie.num_ctx_switches)
def test_num_threads(self):
- self.assertRaises(psutil.ZombieProcess, self.p.num_threads)
+ self.assertRaises(psutil.ZombieProcess, self.zombie.num_threads)
def test_open_files(self):
- self.assertRaises(psutil.ZombieProcess, self.p.open_files)
+ self.assertRaises(psutil.ZombieProcess, self.zombie.open_files)
def test_connections(self):
- self.assertRaises(psutil.ZombieProcess, self.p.connections)
+ self.assertRaises(psutil.ZombieProcess, self.zombie.connections)
def test_num_fds(self):
- self.assertRaises(psutil.ZombieProcess, self.p.num_fds)
+ self.assertRaises(psutil.ZombieProcess, self.zombie.num_fds)
def test_threads(self):
self.assertRaises((psutil.ZombieProcess, psutil.AccessDenied),
- self.p.threads)
+ self.zombie.threads)
@unittest.skipIf(not MACOS, "MACOS only")
diff --git a/psutil/tests/test_testutils.py b/psutil/tests/test_testutils.py
index b2be93ff..85b61aea 100755
--- a/psutil/tests/test_testutils.py
+++ b/psutil/tests/test_testutils.py
@@ -267,11 +267,12 @@ class TestProcessUtils(ProcessTestCase):
assert not psutil.pid_exists(pid)
terminate(pid)
# zombie
- parent, zombie = self.create_zombie_proc()
- terminate(parent)
- terminate(zombie)
- assert not psutil.pid_exists(parent.pid)
- assert not psutil.pid_exists(zombie.pid)
+ if POSIX:
+ parent, zombie = self.create_zombie_proc()
+ terminate(parent)
+ terminate(zombie)
+ assert not psutil.pid_exists(parent.pid)
+ assert not psutil.pid_exists(zombie.pid)
class TestNetUtils(unittest.TestCase):
diff --git a/scripts/internal/winmake.py b/scripts/internal/winmake.py
index c9aa2952..f39d45ac 100755
--- a/scripts/internal/winmake.py
+++ b/scripts/internal/winmake.py
@@ -227,8 +227,13 @@ def build():
# edit mode).
sh('%s -c "import setuptools"' % PYTHON)
+ # "build_ext -i" copies compiled *.pyd files in ./psutil directory in
+ # order to allow "import psutil" when using the interactive interpreter
+ # from within psutil root directory.
+ cmd = [PYTHON, "setup.py", "build_ext", "-i"]
+ if sys.version_info[:2] >= (3, 6) and os.cpu_count() or 1 > 1:
+ cmd += ['--parallel', str(os.cpu_count())]
# Print coloured warnings in real time.
- cmd = [PYTHON, "setup.py", "build"]
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
try:
for line in iter(p.stdout.readline, b''):
@@ -250,10 +255,6 @@ def build():
p.terminate()
p.wait()
- # Copies compiled *.pyd files in ./psutil directory in order to
- # allow "import psutil" when using the interactive interpreter
- # from within this directory.
- sh("%s setup.py build_ext -i" % PYTHON)
# Make sure it actually worked.
sh('%s -c "import psutil"' % PYTHON)
win_colorprint("build + import successful", GREEN)