From 32246daee44a5255f95647854f706dc7cfdbc2bb Mon Sep 17 00:00:00 2001 From: Davanum Srinivas Date: Mon, 28 Sep 2015 21:46:51 -0400 Subject: [PATCH] hacking check for contextlib.nested for py34 support Removed use of contextlib.nested call from codebase as contextlib.nested is not compatible with Python 3. Added hacking check to catch if any new instances are added to the codebase. Change-Id: Ib78102bc013e4cc91ba54d79aa2815f4cf9f446d --- HACKING.rst | 1 + nova/hacking/checks.py | 12 ++++++++++++ nova/tests/unit/test_hacking.py | 25 +++++++++++++++++++++++++ 3 files changed, 38 insertions(+) diff --git a/HACKING.rst b/HACKING.rst index 61785b2e25..5b769c1933 100644 --- a/HACKING.rst +++ b/HACKING.rst @@ -51,6 +51,7 @@ Nova Specific Commandments assertIn/NotIn(A, B) - [N339] Check common raise_feature_not_supported() is used for v2.1 HTTPNotImplemented response. - [N340] Check nova.utils.spawn() is used instead of greenthread.spawn() and eventlet.spawn() +- [N341] contextlib.nested is deprecated Creating Unit Tests ------------------- diff --git a/nova/hacking/checks.py b/nova/hacking/checks.py index ddd9218c82..4f1400ad15 100644 --- a/nova/hacking/checks.py +++ b/nova/hacking/checks.py @@ -98,6 +98,7 @@ decorator_re = re.compile(r"@.*") http_not_implemented_re = re.compile(r"raise .*HTTPNotImplemented\(") spawn_re = re.compile( r".*(eventlet|greenthread)\.(?Pspawn(_n)?)\(.*\)") +contextlib_nested = re.compile(r"^with (contextlib\.)?nested\(") class BaseASTChecker(ast.NodeVisitor): @@ -539,6 +540,16 @@ def check_greenthread_spawns(logical_line, physical_line, filename): yield (0, msg % {'spawn': match.group('spawn_part')}) +def check_no_contextlib_nested(logical_line, filename): + msg = ("N341: contextlib.nested is deprecated. With Python 2.7 and later " + "the with-statement supports multiple nested objects. See https://" + "docs.python.org/2/library/contextlib.html#contextlib.nested for " + "more information. nova.test.nested() is an alternative as well.") + + if contextlib_nested.match(logical_line): + yield(0, msg) + + def factory(register): register(import_no_db_in_virt) register(no_db_session_in_public_api) @@ -565,4 +576,5 @@ def factory(register): register(dict_constructor_with_list_copy) register(assert_equal_in) register(check_http_not_implemented) + register(check_no_contextlib_nested) register(check_greenthread_spawns) diff --git a/nova/tests/unit/test_hacking.py b/nova/tests/unit/test_hacking.py index e4efdb044e..d8f3f8fac9 100644 --- a/nova/tests/unit/test_hacking.py +++ b/nova/tests/unit/test_hacking.py @@ -535,6 +535,31 @@ class HackingTestCase(test.NoDBTestCase): self._assert_has_no_errors(code, checks.check_http_not_implemented, filename=filename) + def test_check_contextlib_use(self): + code = """ + with test.nested( + mock.patch.object(network_model.NetworkInfo, 'hydrate'), + mock.patch.object(objects.InstanceInfoCache, 'save'), + ) as ( + hydrate_mock, save_mock + ) + """ + filename = "nova/api/openstack/compute/v21/test.py" + self._assert_has_no_errors(code, checks.check_no_contextlib_nested, + filename=filename) + code = """ + with contextlib.nested( + mock.patch.object(network_model.NetworkInfo, 'hydrate'), + mock.patch.object(objects.InstanceInfoCache, 'save'), + ) as ( + hydrate_mock, save_mock + ) + """ + filename = "nova/api/openstack/compute/legacy_v2/test.py" + errors = [(1, 0, 'N341')] + self._assert_has_errors(code, checks.check_no_contextlib_nested, + expected_errors=errors, filename=filename) + def test_check_greenthread_spawns(self): errors = [(1, 0, "N340")]