You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			432 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Python
		
	
			
		
		
	
	
			432 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Python
		
	
# Licensed under the Apache License, Version 2.0 (the "License"); you may
 | 
						|
# not use this file except in compliance with the License. You may obtain
 | 
						|
# a copy of the License at
 | 
						|
#
 | 
						|
#     http://www.apache.org/licenses/LICENSE-2.0
 | 
						|
#
 | 
						|
# Unless required by applicable law or agreed to in writing, software
 | 
						|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
						|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
						|
# License for the specific language governing permissions and limitations
 | 
						|
# under the License.
 | 
						|
 | 
						|
import asyncio
 | 
						|
from datetime import timedelta
 | 
						|
from random import random
 | 
						|
import unittest
 | 
						|
 | 
						|
from tornado import gen, queues
 | 
						|
from tornado.gen import TimeoutError
 | 
						|
from tornado.testing import gen_test, AsyncTestCase
 | 
						|
 | 
						|
 | 
						|
class QueueBasicTest(AsyncTestCase):
 | 
						|
    def test_repr_and_str(self):
 | 
						|
        q = queues.Queue(maxsize=1)  # type: queues.Queue[None]
 | 
						|
        self.assertIn(hex(id(q)), repr(q))
 | 
						|
        self.assertNotIn(hex(id(q)), str(q))
 | 
						|
        q.get()
 | 
						|
 | 
						|
        for q_str in repr(q), str(q):
 | 
						|
            self.assertTrue(q_str.startswith("<Queue"))
 | 
						|
            self.assertIn("maxsize=1", q_str)
 | 
						|
            self.assertIn("getters[1]", q_str)
 | 
						|
            self.assertNotIn("putters", q_str)
 | 
						|
            self.assertNotIn("tasks", q_str)
 | 
						|
 | 
						|
        q.put(None)
 | 
						|
        q.put(None)
 | 
						|
        # Now the queue is full, this putter blocks.
 | 
						|
        q.put(None)
 | 
						|
 | 
						|
        for q_str in repr(q), str(q):
 | 
						|
            self.assertNotIn("getters", q_str)
 | 
						|
            self.assertIn("putters[1]", q_str)
 | 
						|
            self.assertIn("tasks=2", q_str)
 | 
						|
 | 
						|
    def test_order(self):
 | 
						|
        q = queues.Queue()  # type: queues.Queue[int]
 | 
						|
        for i in [1, 3, 2]:
 | 
						|
            q.put_nowait(i)
 | 
						|
 | 
						|
        items = [q.get_nowait() for _ in range(3)]
 | 
						|
        self.assertEqual([1, 3, 2], items)
 | 
						|
 | 
						|
    @gen_test
 | 
						|
    def test_maxsize(self):
 | 
						|
        self.assertRaises(TypeError, queues.Queue, maxsize=None)
 | 
						|
        self.assertRaises(ValueError, queues.Queue, maxsize=-1)
 | 
						|
 | 
						|
        q = queues.Queue(maxsize=2)  # type: queues.Queue[int]
 | 
						|
        self.assertTrue(q.empty())
 | 
						|
        self.assertFalse(q.full())
 | 
						|
        self.assertEqual(2, q.maxsize)
 | 
						|
        self.assertTrue(q.put(0).done())
 | 
						|
        self.assertTrue(q.put(1).done())
 | 
						|
        self.assertFalse(q.empty())
 | 
						|
        self.assertTrue(q.full())
 | 
						|
        put2 = q.put(2)
 | 
						|
        self.assertFalse(put2.done())
 | 
						|
        self.assertEqual(0, (yield q.get()))  # Make room.
 | 
						|
        self.assertTrue(put2.done())
 | 
						|
        self.assertFalse(q.empty())
 | 
						|
        self.assertTrue(q.full())
 | 
						|
 | 
						|
 | 
						|
class QueueGetTest(AsyncTestCase):
 | 
						|
    @gen_test
 | 
						|
    def test_blocking_get(self):
 | 
						|
        q = queues.Queue()  # type: queues.Queue[int]
 | 
						|
        q.put_nowait(0)
 | 
						|
        self.assertEqual(0, (yield q.get()))
 | 
						|
 | 
						|
    def test_nonblocking_get(self):
 | 
						|
        q = queues.Queue()  # type: queues.Queue[int]
 | 
						|
        q.put_nowait(0)
 | 
						|
        self.assertEqual(0, q.get_nowait())
 | 
						|
 | 
						|
    def test_nonblocking_get_exception(self):
 | 
						|
        q = queues.Queue()  # type: queues.Queue[int]
 | 
						|
        self.assertRaises(queues.QueueEmpty, q.get_nowait)
 | 
						|
 | 
						|
    @gen_test
 | 
						|
    def test_get_with_putters(self):
 | 
						|
        q = queues.Queue(1)  # type: queues.Queue[int]
 | 
						|
        q.put_nowait(0)
 | 
						|
        put = q.put(1)
 | 
						|
        self.assertEqual(0, (yield q.get()))
 | 
						|
        self.assertIsNone((yield put))
 | 
						|
 | 
						|
    @gen_test
 | 
						|
    def test_blocking_get_wait(self):
 | 
						|
        q = queues.Queue()  # type: queues.Queue[int]
 | 
						|
        q.put(0)
 | 
						|
        self.io_loop.call_later(0.01, q.put_nowait, 1)
 | 
						|
        self.io_loop.call_later(0.02, q.put_nowait, 2)
 | 
						|
        self.assertEqual(0, (yield q.get(timeout=timedelta(seconds=1))))
 | 
						|
        self.assertEqual(1, (yield q.get(timeout=timedelta(seconds=1))))
 | 
						|
 | 
						|
    @gen_test
 | 
						|
    def test_get_timeout(self):
 | 
						|
        q = queues.Queue()  # type: queues.Queue[int]
 | 
						|
        get_timeout = q.get(timeout=timedelta(seconds=0.01))
 | 
						|
        get = q.get()
 | 
						|
        with self.assertRaises(TimeoutError):
 | 
						|
            yield get_timeout
 | 
						|
 | 
						|
        q.put_nowait(0)
 | 
						|
        self.assertEqual(0, (yield get))
 | 
						|
 | 
						|
    @gen_test
 | 
						|
    def test_get_timeout_preempted(self):
 | 
						|
        q = queues.Queue()  # type: queues.Queue[int]
 | 
						|
        get = q.get(timeout=timedelta(seconds=0.01))
 | 
						|
        q.put(0)
 | 
						|
        yield gen.sleep(0.02)
 | 
						|
        self.assertEqual(0, (yield get))
 | 
						|
 | 
						|
    @gen_test
 | 
						|
    def test_get_clears_timed_out_putters(self):
 | 
						|
        q = queues.Queue(1)  # type: queues.Queue[int]
 | 
						|
        # First putter succeeds, remainder block.
 | 
						|
        putters = [q.put(i, timedelta(seconds=0.01)) for i in range(10)]
 | 
						|
        put = q.put(10)
 | 
						|
        self.assertEqual(10, len(q._putters))
 | 
						|
        yield gen.sleep(0.02)
 | 
						|
        self.assertEqual(10, len(q._putters))
 | 
						|
        self.assertFalse(put.done())  # Final waiter is still active.
 | 
						|
        q.put(11)
 | 
						|
        self.assertEqual(0, (yield q.get()))  # get() clears the waiters.
 | 
						|
        self.assertEqual(1, len(q._putters))
 | 
						|
        for putter in putters[1:]:
 | 
						|
            self.assertRaises(TimeoutError, putter.result)
 | 
						|
 | 
						|
    @gen_test
 | 
						|
    def test_get_clears_timed_out_getters(self):
 | 
						|
        q = queues.Queue()  # type: queues.Queue[int]
 | 
						|
        getters = [
 | 
						|
            asyncio.ensure_future(q.get(timedelta(seconds=0.01))) for _ in range(10)
 | 
						|
        ]
 | 
						|
        get = asyncio.ensure_future(q.get())
 | 
						|
        self.assertEqual(11, len(q._getters))
 | 
						|
        yield gen.sleep(0.02)
 | 
						|
        self.assertEqual(11, len(q._getters))
 | 
						|
        self.assertFalse(get.done())  # Final waiter is still active.
 | 
						|
        q.get()  # get() clears the waiters.
 | 
						|
        self.assertEqual(2, len(q._getters))
 | 
						|
        for getter in getters:
 | 
						|
            self.assertRaises(TimeoutError, getter.result)
 | 
						|
 | 
						|
    @gen_test
 | 
						|
    def test_async_for(self):
 | 
						|
        q = queues.Queue()  # type: queues.Queue[int]
 | 
						|
        for i in range(5):
 | 
						|
            q.put(i)
 | 
						|
 | 
						|
        async def f():
 | 
						|
            results = []
 | 
						|
            async for i in q:
 | 
						|
                results.append(i)
 | 
						|
                if i == 4:
 | 
						|
                    return results
 | 
						|
 | 
						|
        results = yield f()
 | 
						|
        self.assertEqual(results, list(range(5)))
 | 
						|
 | 
						|
 | 
						|
class QueuePutTest(AsyncTestCase):
 | 
						|
    @gen_test
 | 
						|
    def test_blocking_put(self):
 | 
						|
        q = queues.Queue()  # type: queues.Queue[int]
 | 
						|
        q.put(0)
 | 
						|
        self.assertEqual(0, q.get_nowait())
 | 
						|
 | 
						|
    def test_nonblocking_put_exception(self):
 | 
						|
        q = queues.Queue(1)  # type: queues.Queue[int]
 | 
						|
        q.put(0)
 | 
						|
        self.assertRaises(queues.QueueFull, q.put_nowait, 1)
 | 
						|
 | 
						|
    @gen_test
 | 
						|
    def test_put_with_getters(self):
 | 
						|
        q = queues.Queue()  # type: queues.Queue[int]
 | 
						|
        get0 = q.get()
 | 
						|
        get1 = q.get()
 | 
						|
        yield q.put(0)
 | 
						|
        self.assertEqual(0, (yield get0))
 | 
						|
        yield q.put(1)
 | 
						|
        self.assertEqual(1, (yield get1))
 | 
						|
 | 
						|
    @gen_test
 | 
						|
    def test_nonblocking_put_with_getters(self):
 | 
						|
        q = queues.Queue()  # type: queues.Queue[int]
 | 
						|
        get0 = q.get()
 | 
						|
        get1 = q.get()
 | 
						|
        q.put_nowait(0)
 | 
						|
        # put_nowait does *not* immediately unblock getters.
 | 
						|
        yield gen.moment
 | 
						|
        self.assertEqual(0, (yield get0))
 | 
						|
        q.put_nowait(1)
 | 
						|
        yield gen.moment
 | 
						|
        self.assertEqual(1, (yield get1))
 | 
						|
 | 
						|
    @gen_test
 | 
						|
    def test_blocking_put_wait(self):
 | 
						|
        q = queues.Queue(1)  # type: queues.Queue[int]
 | 
						|
        q.put_nowait(0)
 | 
						|
 | 
						|
        def get_and_discard():
 | 
						|
            q.get()
 | 
						|
 | 
						|
        self.io_loop.call_later(0.01, get_and_discard)
 | 
						|
        self.io_loop.call_later(0.02, get_and_discard)
 | 
						|
        futures = [q.put(0), q.put(1)]
 | 
						|
        self.assertFalse(any(f.done() for f in futures))
 | 
						|
        yield futures
 | 
						|
 | 
						|
    @gen_test
 | 
						|
    def test_put_timeout(self):
 | 
						|
        q = queues.Queue(1)  # type: queues.Queue[int]
 | 
						|
        q.put_nowait(0)  # Now it's full.
 | 
						|
        put_timeout = q.put(1, timeout=timedelta(seconds=0.01))
 | 
						|
        put = q.put(2)
 | 
						|
        with self.assertRaises(TimeoutError):
 | 
						|
            yield put_timeout
 | 
						|
 | 
						|
        self.assertEqual(0, q.get_nowait())
 | 
						|
        # 1 was never put in the queue.
 | 
						|
        self.assertEqual(2, (yield q.get()))
 | 
						|
 | 
						|
        # Final get() unblocked this putter.
 | 
						|
        yield put
 | 
						|
 | 
						|
    @gen_test
 | 
						|
    def test_put_timeout_preempted(self):
 | 
						|
        q = queues.Queue(1)  # type: queues.Queue[int]
 | 
						|
        q.put_nowait(0)
 | 
						|
        put = q.put(1, timeout=timedelta(seconds=0.01))
 | 
						|
        q.get()
 | 
						|
        yield gen.sleep(0.02)
 | 
						|
        yield put  # No TimeoutError.
 | 
						|
 | 
						|
    @gen_test
 | 
						|
    def test_put_clears_timed_out_putters(self):
 | 
						|
        q = queues.Queue(1)  # type: queues.Queue[int]
 | 
						|
        # First putter succeeds, remainder block.
 | 
						|
        putters = [q.put(i, timedelta(seconds=0.01)) for i in range(10)]
 | 
						|
        put = q.put(10)
 | 
						|
        self.assertEqual(10, len(q._putters))
 | 
						|
        yield gen.sleep(0.02)
 | 
						|
        self.assertEqual(10, len(q._putters))
 | 
						|
        self.assertFalse(put.done())  # Final waiter is still active.
 | 
						|
        q.put(11)  # put() clears the waiters.
 | 
						|
        self.assertEqual(2, len(q._putters))
 | 
						|
        for putter in putters[1:]:
 | 
						|
            self.assertRaises(TimeoutError, putter.result)
 | 
						|
 | 
						|
    @gen_test
 | 
						|
    def test_put_clears_timed_out_getters(self):
 | 
						|
        q = queues.Queue()  # type: queues.Queue[int]
 | 
						|
        getters = [
 | 
						|
            asyncio.ensure_future(q.get(timedelta(seconds=0.01))) for _ in range(10)
 | 
						|
        ]
 | 
						|
        get = asyncio.ensure_future(q.get())
 | 
						|
        q.get()
 | 
						|
        self.assertEqual(12, len(q._getters))
 | 
						|
        yield gen.sleep(0.02)
 | 
						|
        self.assertEqual(12, len(q._getters))
 | 
						|
        self.assertFalse(get.done())  # Final waiters still active.
 | 
						|
        q.put(0)  # put() clears the waiters.
 | 
						|
        self.assertEqual(1, len(q._getters))
 | 
						|
        self.assertEqual(0, (yield get))
 | 
						|
        for getter in getters:
 | 
						|
            self.assertRaises(TimeoutError, getter.result)
 | 
						|
 | 
						|
    @gen_test
 | 
						|
    def test_float_maxsize(self):
 | 
						|
        # If a float is passed for maxsize, a reasonable limit should
 | 
						|
        # be enforced, instead of being treated as unlimited.
 | 
						|
        # It happens to be rounded up.
 | 
						|
        # http://bugs.python.org/issue21723
 | 
						|
        q = queues.Queue(maxsize=1.3)  # type: ignore
 | 
						|
        self.assertTrue(q.empty())
 | 
						|
        self.assertFalse(q.full())
 | 
						|
        q.put_nowait(0)
 | 
						|
        q.put_nowait(1)
 | 
						|
        self.assertFalse(q.empty())
 | 
						|
        self.assertTrue(q.full())
 | 
						|
        self.assertRaises(queues.QueueFull, q.put_nowait, 2)
 | 
						|
        self.assertEqual(0, q.get_nowait())
 | 
						|
        self.assertFalse(q.empty())
 | 
						|
        self.assertFalse(q.full())
 | 
						|
 | 
						|
        yield q.put(2)
 | 
						|
        put = q.put(3)
 | 
						|
        self.assertFalse(put.done())
 | 
						|
        self.assertEqual(1, (yield q.get()))
 | 
						|
        yield put
 | 
						|
        self.assertTrue(q.full())
 | 
						|
 | 
						|
 | 
						|
class QueueJoinTest(AsyncTestCase):
 | 
						|
    queue_class = queues.Queue
 | 
						|
 | 
						|
    def test_task_done_underflow(self):
 | 
						|
        q = self.queue_class()  # type: queues.Queue
 | 
						|
        self.assertRaises(ValueError, q.task_done)
 | 
						|
 | 
						|
    @gen_test
 | 
						|
    def test_task_done(self):
 | 
						|
        q = self.queue_class()  # type: queues.Queue
 | 
						|
        for i in range(100):
 | 
						|
            q.put_nowait(i)
 | 
						|
 | 
						|
        self.accumulator = 0
 | 
						|
 | 
						|
        @gen.coroutine
 | 
						|
        def worker():
 | 
						|
            while True:
 | 
						|
                item = yield q.get()
 | 
						|
                self.accumulator += item
 | 
						|
                q.task_done()
 | 
						|
                yield gen.sleep(random() * 0.01)
 | 
						|
 | 
						|
        # Two coroutines share work.
 | 
						|
        worker()
 | 
						|
        worker()
 | 
						|
        yield q.join()
 | 
						|
        self.assertEqual(sum(range(100)), self.accumulator)
 | 
						|
 | 
						|
    @gen_test
 | 
						|
    def test_task_done_delay(self):
 | 
						|
        # Verify it is task_done(), not get(), that unblocks join().
 | 
						|
        q = self.queue_class()  # type: queues.Queue
 | 
						|
        q.put_nowait(0)
 | 
						|
        join = asyncio.ensure_future(q.join())
 | 
						|
        self.assertFalse(join.done())
 | 
						|
        yield q.get()
 | 
						|
        self.assertFalse(join.done())
 | 
						|
        yield gen.moment
 | 
						|
        self.assertFalse(join.done())
 | 
						|
        q.task_done()
 | 
						|
        self.assertTrue(join.done())
 | 
						|
 | 
						|
    @gen_test
 | 
						|
    def test_join_empty_queue(self):
 | 
						|
        q = self.queue_class()  # type: queues.Queue
 | 
						|
        yield q.join()
 | 
						|
        yield q.join()
 | 
						|
 | 
						|
    @gen_test
 | 
						|
    def test_join_timeout(self):
 | 
						|
        q = self.queue_class()  # type: queues.Queue
 | 
						|
        q.put(0)
 | 
						|
        with self.assertRaises(TimeoutError):
 | 
						|
            yield q.join(timeout=timedelta(seconds=0.01))
 | 
						|
 | 
						|
 | 
						|
class PriorityQueueJoinTest(QueueJoinTest):
 | 
						|
    queue_class = queues.PriorityQueue
 | 
						|
 | 
						|
    @gen_test
 | 
						|
    def test_order(self):
 | 
						|
        q = self.queue_class(maxsize=2)
 | 
						|
        q.put_nowait((1, "a"))
 | 
						|
        q.put_nowait((0, "b"))
 | 
						|
        self.assertTrue(q.full())
 | 
						|
        q.put((3, "c"))
 | 
						|
        q.put((2, "d"))
 | 
						|
        self.assertEqual((0, "b"), q.get_nowait())
 | 
						|
        self.assertEqual((1, "a"), (yield q.get()))
 | 
						|
        self.assertEqual((2, "d"), q.get_nowait())
 | 
						|
        self.assertEqual((3, "c"), (yield q.get()))
 | 
						|
        self.assertTrue(q.empty())
 | 
						|
 | 
						|
 | 
						|
class LifoQueueJoinTest(QueueJoinTest):
 | 
						|
    queue_class = queues.LifoQueue
 | 
						|
 | 
						|
    @gen_test
 | 
						|
    def test_order(self):
 | 
						|
        q = self.queue_class(maxsize=2)
 | 
						|
        q.put_nowait(1)
 | 
						|
        q.put_nowait(0)
 | 
						|
        self.assertTrue(q.full())
 | 
						|
        q.put(3)
 | 
						|
        q.put(2)
 | 
						|
        self.assertEqual(3, q.get_nowait())
 | 
						|
        self.assertEqual(2, (yield q.get()))
 | 
						|
        self.assertEqual(0, q.get_nowait())
 | 
						|
        self.assertEqual(1, (yield q.get()))
 | 
						|
        self.assertTrue(q.empty())
 | 
						|
 | 
						|
 | 
						|
class ProducerConsumerTest(AsyncTestCase):
 | 
						|
    @gen_test
 | 
						|
    def test_producer_consumer(self):
 | 
						|
        q = queues.Queue(maxsize=3)  # type: queues.Queue[int]
 | 
						|
        history = []
 | 
						|
 | 
						|
        # We don't yield between get() and task_done(), so get() must wait for
 | 
						|
        # the next tick. Otherwise we'd immediately call task_done and unblock
 | 
						|
        # join() before q.put() resumes, and we'd only process the first four
 | 
						|
        # items.
 | 
						|
        @gen.coroutine
 | 
						|
        def consumer():
 | 
						|
            while True:
 | 
						|
                history.append((yield q.get()))
 | 
						|
                q.task_done()
 | 
						|
 | 
						|
        @gen.coroutine
 | 
						|
        def producer():
 | 
						|
            for item in range(10):
 | 
						|
                yield q.put(item)
 | 
						|
 | 
						|
        consumer()
 | 
						|
        yield producer()
 | 
						|
        yield q.join()
 | 
						|
        self.assertEqual(list(range(10)), history)
 | 
						|
 | 
						|
 | 
						|
if __name__ == "__main__":
 | 
						|
    unittest.main()
 |