diff --git a/HACKING.rst b/HACKING.rst index 8f84aa820f09..e42e0b929467 100644 --- a/HACKING.rst +++ b/HACKING.rst @@ -79,6 +79,8 @@ Nova Specific Commandments - [N372] Don't use the setDaemon method. Use the daemon attribute instead. - [N373] Don't use eventlet specific concurrency primitives. Use the one from stdlib instead. E.g. eventlet.sleep => time.sleep +- [N374] Don't use time.sleep(0) to trigger eventlet yielding. + Use nova.utils.cooperative_yield() instead. Creating Unit Tests ------------------- diff --git a/nova/hacking/checks.py b/nova/hacking/checks.py index 6fed5c222898..8b6b9714969d 100644 --- a/nova/hacking/checks.py +++ b/nova/hacking/checks.py @@ -145,6 +145,7 @@ six_re = re.compile(r"^(import six(\..*)?|from six(\..*)? import .*)$") set_daemon_re = re.compile(r"\.setDaemon\(") eventlet_stdlib_primitives_re = re.compile( r".*(eventlet|greenthread)\.sleep\(.*") +eventlet_yield_re = re.compile(r".*time\.sleep\(0\).*") class BaseASTChecker(ast.NodeVisitor): @@ -1111,10 +1112,26 @@ def check_eventlet_primitives(logical_line, filename): N373 """ msg = ( - "N373: Use the stdlib concurrency primitive instead of the Eventelt " + "N373: Use the stdlib concurrency primitive instead of the Eventlet " "specific one") match = re.match(eventlet_stdlib_primitives_re, logical_line) if match: yield (0, msg) + + +@core.flake8ext +def check_eventlet_yield(logical_line, filename): + """Check for use of time.sleep(0) triggering eventlet yield + + N374 + """ + msg = ( + "N374: Use the nova.utils.cooperative_yield instead of time.sleep(0) " + "to trigger an eventlet context switch") + + match = re.match(eventlet_yield_re, logical_line) + + if match: + yield (0, msg) diff --git a/nova/tests/unit/test_hacking.py b/nova/tests/unit/test_hacking.py index 9fdebbf41c9c..e9a88423adf5 100644 --- a/nova/tests/unit/test_hacking.py +++ b/nova/tests/unit/test_hacking.py @@ -1078,3 +1078,16 @@ class HackingTestCase(test.NoDBTestCase): time.sleep(1) """ self._assert_has_no_errors(code, checks.check_eventlet_primitives) + + def test_check_eventlet_yield(self): + code = """ + time.sleep(0) + """ + errors = [(x + 1, 0, 'N374') for x in range(1)] + self._assert_has_errors( + code, checks.check_eventlet_yield, expected_errors=errors) + + code = """ + time.sleep(1) + """ + self._assert_has_no_errors(code, checks.check_eventlet_yield) diff --git a/nova/utils.py b/nova/utils.py index 38fdd79fefd8..2fcbb5f47928 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -85,7 +85,7 @@ def cooperative_yield(): # TODO(ksambor) Remove all cooperative_yield calls after dropping Eventlet # support. if not concurrency_mode_threading(): - time.sleep(0) + time.sleep(0) # noqa: N374 def destroy_default_executor(): diff --git a/tox.ini b/tox.ini index 38d59798784b..115c86d2cc1a 100644 --- a/tox.ini +++ b/tox.ini @@ -380,6 +380,7 @@ extension = N371 = checks:import_stock_mock N372 = checks:check_set_daemon N373 = checks:check_eventlet_primitives + N374 = checks:check_eventlet_yield paths = ./nova/hacking