From 8eba4f8c8f463c37c3b1a38e3f956be0abd49e59 Mon Sep 17 00:00:00 2001 From: Romain de Joux Date: Thu, 22 Nov 2018 11:24:02 +0100 Subject: [PATCH] Use eventlet.patcher.original to get Python select module in get_hub get_hub function was added in commit b155da42 with the idea to bypass eventlet automatic hub selection that prefers epoll if available by default. Since version 0.20.0 eventlet removed select.poll() function in its patched select module (eventlet.green.select), see: - https://github.com/eventlet/eventlet/commit/614a20462 So if eventlet monkey patching is done before a get_hub() call (as now in wsgi.py since commit c9410c7d) if we use 'import select' we get the eventlet version that don't have poll attribute. To prevent that we use eventlet.patcher.original function to get python select module to test if poll() is available on current platform. Change-Id: I69b3db3951b3d3b6583845978deb2883492e7f0f Closes-Bug: 1804627 (cherry picked from commit 4809884d9f20eb0be6df9840e4e6a75577fecc4c) --- swift/common/utils.py | 11 ++++++++++- test/unit/common/test_utils.py | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/swift/common/utils.py b/swift/common/utils.py index c750678736..9155a415ce 100644 --- a/swift/common/utils.py +++ b/swift/common/utils.py @@ -2015,9 +2015,18 @@ def get_hub(): In contrast, both poll() and select() specify the set of interesting file descriptors with each call, so there's no problem with forking. + + As eventlet monkey patching is now done before call get_hub() in wsgi.py + if we use 'import select' we get the eventlet version, but since version + 0.20.0 eventlet removed select.poll() function in patched select (see: + http://eventlet.net/doc/changelog.html and + https://github.com/eventlet/eventlet/commit/614a20462). + + We use eventlet.patcher.original function to get python select module + to test if poll() is available on platform. """ try: - import select + select = eventlet.patcher.original('select') if hasattr(select, "poll"): return "poll" return "selects" diff --git a/test/unit/common/test_utils.py b/test/unit/common/test_utils.py index 3d4bd9f7d9..fde083a335 100644 --- a/test/unit/common/test_utils.py +++ b/test/unit/common/test_utils.py @@ -4542,6 +4542,38 @@ class TestFileLikeIter(unittest.TestCase): iter_file.close() self.assertTrue(iter_file.closed) + def test_get_hub(self): + # This test mock the eventlet.green.select module without poll + # as in eventlet > 0.20 + # https://github.com/eventlet/eventlet/commit/614a20462 + # We add __original_module_select to sys.modules to mock usage + # of eventlet.patcher.original + + class SelectWithPoll(object): + def poll(): + pass + + class SelectWithoutPoll(object): + pass + + # Platform with poll() that call get_hub before eventlet patching + with mock.patch.dict('sys.modules', + {'select': SelectWithPoll, + '__original_module_select': SelectWithPoll}): + self.assertEqual(utils.get_hub(), 'poll') + + # Platform with poll() that call get_hub after eventlet patching + with mock.patch.dict('sys.modules', + {'select': SelectWithoutPoll, + '__original_module_select': SelectWithPoll}): + self.assertEqual(utils.get_hub(), 'poll') + + # Platform without poll() -- before or after patching doesn't matter + with mock.patch.dict('sys.modules', + {'select': SelectWithoutPoll, + '__original_module_select': SelectWithoutPoll}): + self.assertEqual(utils.get_hub(), 'selects') + class TestStatsdLogging(unittest.TestCase): def setUp(self):