Commit 3f5b9088 authored by Pablo Galindo's avatar Pablo Galindo Committed by GitHub

bpo-37394: Fix pure Python implementation of the queue module (GH-14351)

parent b51b7137
...@@ -14,7 +14,7 @@ __all__ = ['Empty', 'Full', 'Queue', 'PriorityQueue', 'LifoQueue', 'SimpleQueue' ...@@ -14,7 +14,7 @@ __all__ = ['Empty', 'Full', 'Queue', 'PriorityQueue', 'LifoQueue', 'SimpleQueue'
try: try:
from _queue import Empty from _queue import Empty
except AttributeError: except ImportError:
class Empty(Exception): class Empty(Exception):
'Exception raised by Queue.get(block=0)/get_nowait().' 'Exception raised by Queue.get(block=0)/get_nowait().'
pass pass
......
# Some simple queue module tests, plus some failure conditions # Some simple queue module tests, plus some failure conditions
# to ensure the Queue locks remain stable. # to ensure the Queue locks remain stable.
import itertools import itertools
import queue
import random import random
import threading import threading
import time import time
...@@ -9,11 +8,9 @@ import unittest ...@@ -9,11 +8,9 @@ import unittest
import weakref import weakref
from test import support from test import support
py_queue = support.import_fresh_module('queue', blocked=['_queue'])
try: c_queue = support.import_fresh_module('queue', fresh=['_queue'])
import _queue need_c_queue = unittest.skipUnless(c_queue, "No _queue module found")
except ImportError:
_queue = None
QUEUE_SIZE = 5 QUEUE_SIZE = 5
...@@ -120,12 +117,12 @@ class BaseQueueTestMixin(BlockingTestMixin): ...@@ -120,12 +117,12 @@ class BaseQueueTestMixin(BlockingTestMixin):
try: try:
q.put(full, block=0) q.put(full, block=0)
self.fail("Didn't appear to block with a full queue") self.fail("Didn't appear to block with a full queue")
except queue.Full: except self.queue.Full:
pass pass
try: try:
q.put(full, timeout=0.01) q.put(full, timeout=0.01)
self.fail("Didn't appear to time-out with a full queue") self.fail("Didn't appear to time-out with a full queue")
except queue.Full: except self.queue.Full:
pass pass
# Test a blocking put # Test a blocking put
self.do_blocking_test(q.put, (full,), q.get, ()) self.do_blocking_test(q.put, (full,), q.get, ())
...@@ -137,12 +134,12 @@ class BaseQueueTestMixin(BlockingTestMixin): ...@@ -137,12 +134,12 @@ class BaseQueueTestMixin(BlockingTestMixin):
try: try:
q.get(block=0) q.get(block=0)
self.fail("Didn't appear to block with an empty queue") self.fail("Didn't appear to block with an empty queue")
except queue.Empty: except self.queue.Empty:
pass pass
try: try:
q.get(timeout=0.01) q.get(timeout=0.01)
self.fail("Didn't appear to time-out with an empty queue") self.fail("Didn't appear to time-out with an empty queue")
except queue.Empty: except self.queue.Empty:
pass pass
# Test a blocking get # Test a blocking get
self.do_blocking_test(q.get, (), q.put, ('empty',)) self.do_blocking_test(q.get, (), q.put, ('empty',))
...@@ -218,12 +215,12 @@ class BaseQueueTestMixin(BlockingTestMixin): ...@@ -218,12 +215,12 @@ class BaseQueueTestMixin(BlockingTestMixin):
q = self.type2test(QUEUE_SIZE) q = self.type2test(QUEUE_SIZE)
for i in range(QUEUE_SIZE): for i in range(QUEUE_SIZE):
q.put_nowait(1) q.put_nowait(1)
with self.assertRaises(queue.Full): with self.assertRaises(self.queue.Full):
q.put_nowait(1) q.put_nowait(1)
for i in range(QUEUE_SIZE): for i in range(QUEUE_SIZE):
q.get_nowait() q.get_nowait()
with self.assertRaises(queue.Empty): with self.assertRaises(self.queue.Empty):
q.get_nowait() q.get_nowait()
def test_shrinking_queue(self): def test_shrinking_queue(self):
...@@ -232,45 +229,88 @@ class BaseQueueTestMixin(BlockingTestMixin): ...@@ -232,45 +229,88 @@ class BaseQueueTestMixin(BlockingTestMixin):
q.put(1) q.put(1)
q.put(2) q.put(2)
q.put(3) q.put(3)
with self.assertRaises(queue.Full): with self.assertRaises(self.queue.Full):
q.put_nowait(4) q.put_nowait(4)
self.assertEqual(q.qsize(), 3) self.assertEqual(q.qsize(), 3)
q.maxsize = 2 # shrink the queue q.maxsize = 2 # shrink the queue
with self.assertRaises(queue.Full): with self.assertRaises(self.queue.Full):
q.put_nowait(4) q.put_nowait(4)
class QueueTest(BaseQueueTestMixin, unittest.TestCase): class QueueTest(BaseQueueTestMixin):
type2test = queue.Queue
def setUp(self):
self.type2test = self.queue.Queue
super().setUp()
class PyQueueTest(QueueTest, unittest.TestCase):
queue = py_queue
@need_c_queue
class CQueueTest(QueueTest, unittest.TestCase):
queue = c_queue
class LifoQueueTest(BaseQueueTestMixin):
def setUp(self):
self.type2test = self.queue.LifoQueue
super().setUp()
class PyLifoQueueTest(LifoQueueTest, unittest.TestCase):
queue = py_queue
@need_c_queue
class CLifoQueueTest(LifoQueueTest, unittest.TestCase):
queue = c_queue
class PriorityQueueTest(BaseQueueTestMixin):
def setUp(self):
self.type2test = self.queue.PriorityQueue
super().setUp()
class LifoQueueTest(BaseQueueTestMixin, unittest.TestCase): class PyPriorityQueueTest(PriorityQueueTest, unittest.TestCase):
type2test = queue.LifoQueue queue = py_queue
class PriorityQueueTest(BaseQueueTestMixin, unittest.TestCase):
type2test = queue.PriorityQueue
@need_c_queue
class CPriorityQueueTest(PriorityQueueTest, unittest.TestCase):
queue = c_queue
# A Queue subclass that can provoke failure at a moment's notice :) # A Queue subclass that can provoke failure at a moment's notice :)
class FailingQueueException(Exception): class FailingQueueException(Exception): pass
pass
class FailingQueueTest(BlockingTestMixin):
class FailingQueue(queue.Queue):
def __init__(self, *args): def setUp(self):
self.fail_next_put = False
self.fail_next_get = False Queue = self.queue.Queue
queue.Queue.__init__(self, *args)
def _put(self, item): class FailingQueue(Queue):
if self.fail_next_put: def __init__(self, *args):
self.fail_next_put = False self.fail_next_put = False
raise FailingQueueException("You Lose") self.fail_next_get = False
return queue.Queue._put(self, item) Queue.__init__(self, *args)
def _get(self): def _put(self, item):
if self.fail_next_get: if self.fail_next_put:
self.fail_next_get = False self.fail_next_put = False
raise FailingQueueException("You Lose") raise FailingQueueException("You Lose")
return queue.Queue._get(self) return Queue._put(self, item)
def _get(self):
class FailingQueueTest(BlockingTestMixin, unittest.TestCase): if self.fail_next_get:
self.fail_next_get = False
raise FailingQueueException("You Lose")
return Queue._get(self)
self.FailingQueue = FailingQueue
super().setUp()
def failing_queue_test(self, q): def failing_queue_test(self, q):
if q.qsize(): if q.qsize():
...@@ -354,13 +394,24 @@ class FailingQueueTest(BlockingTestMixin, unittest.TestCase): ...@@ -354,13 +394,24 @@ class FailingQueueTest(BlockingTestMixin, unittest.TestCase):
self.assertTrue(not q.qsize(), "Queue should be empty") self.assertTrue(not q.qsize(), "Queue should be empty")
def test_failing_queue(self): def test_failing_queue(self):
# Test to make sure a queue is functioning correctly. # Test to make sure a queue is functioning correctly.
# Done twice to the same instance. # Done twice to the same instance.
q = FailingQueue(QUEUE_SIZE) q = self.FailingQueue(QUEUE_SIZE)
self.failing_queue_test(q) self.failing_queue_test(q)
self.failing_queue_test(q) self.failing_queue_test(q)
class PyFailingQueueTest(FailingQueueTest, unittest.TestCase):
queue = py_queue
@need_c_queue
class CFailingQueueTest(FailingQueueTest, unittest.TestCase):
queue = c_queue
class BaseSimpleQueueTest: class BaseSimpleQueueTest:
def setUp(self): def setUp(self):
...@@ -388,7 +439,7 @@ class BaseSimpleQueueTest: ...@@ -388,7 +439,7 @@ class BaseSimpleQueueTest:
while True: while True:
try: try:
val = q.get(block=False) val = q.get(block=False)
except queue.Empty: except self.queue.Empty:
time.sleep(1e-5) time.sleep(1e-5)
else: else:
break break
...@@ -401,7 +452,7 @@ class BaseSimpleQueueTest: ...@@ -401,7 +452,7 @@ class BaseSimpleQueueTest:
while True: while True:
try: try:
val = q.get(timeout=1e-5) val = q.get(timeout=1e-5)
except queue.Empty: except self.queue.Empty:
pass pass
else: else:
break break
...@@ -470,11 +521,11 @@ class BaseSimpleQueueTest: ...@@ -470,11 +521,11 @@ class BaseSimpleQueueTest:
self.assertTrue(q.empty()) self.assertTrue(q.empty())
self.assertEqual(q.qsize(), 0) self.assertEqual(q.qsize(), 0)
with self.assertRaises(queue.Empty): with self.assertRaises(self.queue.Empty):
q.get(block=False) q.get(block=False)
with self.assertRaises(queue.Empty): with self.assertRaises(self.queue.Empty):
q.get(timeout=1e-3) q.get(timeout=1e-3)
with self.assertRaises(queue.Empty): with self.assertRaises(self.queue.Empty):
q.get_nowait() q.get_nowait()
self.assertTrue(q.empty()) self.assertTrue(q.empty())
self.assertEqual(q.qsize(), 0) self.assertEqual(q.qsize(), 0)
...@@ -541,18 +592,25 @@ class BaseSimpleQueueTest: ...@@ -541,18 +592,25 @@ class BaseSimpleQueueTest:
class PySimpleQueueTest(BaseSimpleQueueTest, unittest.TestCase): class PySimpleQueueTest(BaseSimpleQueueTest, unittest.TestCase):
type2test = queue._PySimpleQueue
queue = py_queue
def setUp(self):
self.type2test = self.queue._PySimpleQueue
super().setUp()
@unittest.skipIf(_queue is None, "No _queue module found")
@need_c_queue
class CSimpleQueueTest(BaseSimpleQueueTest, unittest.TestCase): class CSimpleQueueTest(BaseSimpleQueueTest, unittest.TestCase):
queue = c_queue
def setUp(self): def setUp(self):
self.type2test = _queue.SimpleQueue self.type2test = self.queue.SimpleQueue
super().setUp() super().setUp()
def test_is_default(self): def test_is_default(self):
self.assertIs(self.type2test, queue.SimpleQueue) self.assertIs(self.type2test, self.queue.SimpleQueue)
self.assertIs(self.type2test, self.queue.SimpleQueue)
def test_reentrancy(self): def test_reentrancy(self):
# bpo-14976: put() may be called reentrantly in an asynchronous # bpo-14976: put() may be called reentrantly in an asynchronous
......
Fix a bug that was causing the :mod:`queue` module to fail if the
accelerator module was not available. Patch by Pablo Galindo.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment