from __future__ import print_function from time import time, sleep import contextlib import random import weakref import gc import gevent.testing as greentest import gevent.threadpool from gevent.threadpool import ThreadPool import gevent from gevent.testing import ExpectedException from gevent.testing import PYPY # pylint:disable=too-many-ancestors @contextlib.contextmanager def disabled_gc(): was_enabled = gc.isenabled() gc.disable() try: yield finally: if was_enabled: gc.enable() class TestCase(greentest.TestCase): # These generally need more time __timeout__ = greentest.LARGE_TIMEOUT pool = None ClassUnderTest = ThreadPool def _FUT(self): return self.ClassUnderTest def _makeOne(self, size, increase=greentest.RUN_LEAKCHECKS): self.pool = pool = self._FUT()(size) if increase: # Max size to help eliminate false positives self.pool.size = size return pool def cleanup(self): pool = self.pool if pool is not None: kill = getattr(pool, 'kill', None) or getattr(pool, 'shutdown') kill() del kill del self.pool if greentest.RUN_LEAKCHECKS: # Each worker thread created a greenlet object and switched to it. # It's a custom subclass, but even if it's not, it appears that # the root greenlet for the new thread sticks around until there's a # gc. Simply calling 'getcurrent()' is enough to "leak" a greenlet.greenlet # and a weakref. for _ in range(3): gc.collect() class PoolBasicTests(TestCase): def test_execute_async(self): pool = self._makeOne(2) r = [] first = pool.spawn(r.append, 1) first.get() self.assertEqual(r, [1]) gevent.sleep(0) pool.apply_async(r.append, (2, )) self.assertEqual(r, [1]) pool.apply_async(r.append, (3, )) self.assertEqual(r, [1]) pool.apply_async(r.append, (4, )) self.assertEqual(r, [1]) gevent.sleep(0.01) self.assertEqualFlakyRaceCondition(sorted(r), [1, 2, 3, 4]) def test_apply(self): pool = self._makeOne(1) result = pool.apply(lambda a: ('foo', a), (1, )) self.assertEqual(result, ('foo', 1)) def test_apply_raises(self): pool = self._makeOne(1) def raiser(): raise ExpectedException() with self.assertRaises(ExpectedException): pool.apply(raiser) # Don't let the metaclass automatically force any error # that reaches the hub from a spawned greenlet to become # fatal; that defeats the point of the test. test_apply_raises.error_fatal = False def test_init_valueerror(self): self.switch_expected = False with self.assertRaises(ValueError): self._makeOne(-1) # # tests from standard library test/test_multiprocessing.py class TimingWrapper(object): def __init__(self, the_func): self.func = the_func self.elapsed = None def __call__(self, *args, **kwds): t = time() try: return self.func(*args, **kwds) finally: self.elapsed = time() - t def sqr(x, wait=0.0): sleep(wait) return x * x def sqr_random_sleep(x): sleep(random.random() * 0.1) return x * x TIMEOUT1, TIMEOUT2, TIMEOUT3 = 0.082, 0.035, 0.14 class _AbstractPoolTest(TestCase): size = 1 MAP_IS_GEN = False def setUp(self): greentest.TestCase.setUp(self) self._makeOne(self.size) @greentest.ignores_leakcheck def test_map(self): pmap = self.pool.map if self.MAP_IS_GEN: pmap = lambda f, i: list(self.pool.map(f, i)) self.assertEqual(pmap(sqr, range(10)), list(map(sqr, range(10)))) self.assertEqual(pmap(sqr, range(100)), list(map(sqr, range(100)))) self.pool.kill() del self.pool del pmap SMALL_RANGE = 10 LARGE_RANGE = 1000 if (greentest.PYPY and (greentest.WIN or greentest.RUN_COVERAGE)) or greentest.RUN_LEAKCHECKS: # PyPy 5.10 is *really* slow at spawning or switching between # threads (especially on Windows or when coverage is enabled) Tests that happen # instantaneously on other platforms time out due to the overhead. # Leakchecks also take much longer due to all the calls into the GC, # most especially on Python 3 LARGE_RANGE = 50 class TestPool(_AbstractPoolTest): def test_greenlet_class(self): from greenlet import getcurrent from gevent.threadpool import _WorkerGreenlet worker_greenlet = self.pool.apply(getcurrent) self.assertIsInstance(worker_greenlet, _WorkerGreenlet) r = repr(worker_greenlet) self.assertIn('ThreadPoolWorker', r) self.assertIn('thread_ident', r) self.assertIn('hub=', r) from gevent.util import format_run_info info = '\n'.join(format_run_info()) self.assertIn("