Merge "Update hacking for Python3"
diff --git a/HACKING.rst b/HACKING.rst
index 204b3c7..f8be0ad 100644
--- a/HACKING.rst
+++ b/HACKING.rst
@@ -12,7 +12,6 @@
   tempest/scenario tests
 - [T104] Scenario tests require a services decorator
 - [T105] Tests cannot use setUpClass/tearDownClass
-- [T106] vim configuration should not be kept in source files.
 - [T107] Check that a service tag isn't in the module path
 - [T108] Check no hyphen at the end of rand_name() argument
 - [T109] Cannot use testtools.skip decorator; instead use
diff --git a/tempest/api/volume/test_volume_absolute_limits.py b/tempest/api/volume/test_volume_absolute_limits.py
index 00a3375..4d64a95 100644
--- a/tempest/api/volume/test_volume_absolute_limits.py
+++ b/tempest/api/volume/test_volume_absolute_limits.py
@@ -23,7 +23,7 @@
 # NOTE(zhufl): This inherits from BaseVolumeAdminTest because
 # it requires force_tenant_isolation=True, which need admin
 # credentials to create non-admin users for the tests.
-class AbsoluteLimitsTests(base.BaseVolumeAdminTest):  # noqa
+class AbsoluteLimitsTests(base.BaseVolumeAdminTest):  # noqa: T115
 
     # avoid existing volumes of pre-defined tenant
     force_tenant_isolation = True
diff --git a/tempest/api/volume/test_volumes_snapshots_list.py b/tempest/api/volume/test_volumes_snapshots_list.py
index 8a416ea..f4f039c 100644
--- a/tempest/api/volume/test_volumes_snapshots_list.py
+++ b/tempest/api/volume/test_volumes_snapshots_list.py
@@ -109,7 +109,7 @@
         snap_list = self.snapshots_client.list_snapshots(
             sort_key=sort_key, sort_dir=sort_dir)['snapshots']
         self.assertNotEmpty(snap_list)
-        if sort_key is 'display_name':
+        if sort_key == 'display_name':
             sort_key = 'name'
         # Note: On Cinder API, 'display_name' works as a sort key
         # on a request, a volume name appears as 'name' on the response.
diff --git a/tempest/hacking/checks.py b/tempest/hacking/checks.py
index 2c40cb1..6a97a00 100644
--- a/tempest/hacking/checks.py
+++ b/tempest/hacking/checks.py
@@ -15,6 +15,7 @@
 import os
 import re
 
+from hacking import core
 import pycodestyle
 
 
@@ -25,7 +26,6 @@
 TEST_DEFINITION = re.compile(r'^\s*def test.*')
 SETUP_TEARDOWN_CLASS_DEFINITION = re.compile(r'^\s+def (setUp|tearDown)Class')
 SCENARIO_DECORATOR = re.compile(r'\s*@.*services\((.*)\)')
-VI_HEADER_RE = re.compile(r"^#\s+vim?:.+")
 RAND_NAME_HYPHEN_RE = re.compile(r".*rand_name\(.+[\-\_][\"\']\)")
 mutable_default_args = re.compile(r"^\s*def .+\((.+=\{\}|.+=\[\])")
 TESTTOOLS_SKIP_DECORATOR = re.compile(r'\s*@testtools\.skip\((.*)\)')
@@ -39,6 +39,7 @@
 _HAVE_NEGATIVE_DECORATOR = False
 
 
+@core.flake8ext
 def import_no_clients_in_api_and_scenario_tests(physical_line, filename):
     """Check for client imports from tempest/api & tempest/scenario tests
 
@@ -53,6 +54,7 @@
                      " in tempest/api/* or tempest/scenario/* tests"))
 
 
+@core.flake8ext
 def scenario_tests_need_service_tags(physical_line, filename,
                                      previous_logical):
     """Check that scenario tests have service tags
@@ -67,6 +69,7 @@
                         "T104: Scenario tests require a service decorator")
 
 
+@core.flake8ext
 def no_setup_teardown_class_for_tests(physical_line, filename):
 
     if pycodestyle.noqa(physical_line):
@@ -80,20 +83,7 @@
                 "T105: (setUp|tearDown)Class can not be used in tests")
 
 
-def no_vi_headers(physical_line, line_number, lines):
-    """Check for vi editor configuration in source files.
-
-    By default vi modelines can only appear in the first or
-    last 5 lines of a source file.
-
-    T106
-    """
-    # NOTE(gilliard): line_number is 1-indexed
-    if line_number <= 5 or line_number > len(lines) - 5:
-        if VI_HEADER_RE.match(physical_line):
-            return 0, "T106: Don't put vi configuration in source files"
-
-
+@core.flake8ext
 def service_tags_not_in_module_path(physical_line, filename):
     """Check that a service tag isn't in the module path
 
@@ -117,6 +107,7 @@
                             "T107: service tag should not be in path")
 
 
+@core.flake8ext
 def no_hyphen_at_end_of_rand_name(logical_line, filename):
     """Check no hyphen at the end of rand_name() argument
 
@@ -127,6 +118,7 @@
         return 0, msg
 
 
+@core.flake8ext
 def no_mutable_default_args(logical_line):
     """Check that mutable object isn't used as default argument
 
@@ -137,6 +129,7 @@
         yield (0, msg)
 
 
+@core.flake8ext
 def no_testtools_skip_decorator(logical_line):
     """Check that methods do not have the testtools.skip decorator
 
@@ -170,7 +163,8 @@
     return True
 
 
-def get_resources_on_service_clients(logical_line, physical_line, filename,
+@core.flake8ext
+def get_resources_on_service_clients(physical_line, logical_line, filename,
                                      line_number, lines):
     """Check that service client names of GET should be consistent
 
@@ -197,7 +191,8 @@
         yield (0, msg)
 
 
-def delete_resources_on_service_clients(logical_line, physical_line, filename,
+@core.flake8ext
+def delete_resources_on_service_clients(physical_line, logical_line, filename,
                                         line_number, lines):
     """Check that service client names of DELETE should be consistent
 
@@ -223,6 +218,7 @@
         yield (0, msg)
 
 
+@core.flake8ext
 def dont_import_local_tempest_into_lib(logical_line, filename):
     """Check that tempest.lib should not import local tempest code
 
@@ -244,6 +240,7 @@
     yield (0, msg)
 
 
+@core.flake8ext
 def use_rand_uuid_instead_of_uuid4(logical_line, filename):
     """Check that tests use data_utils.rand_uuid() instead of uuid.uuid4()
 
@@ -260,6 +257,7 @@
     yield (0, msg)
 
 
+@core.flake8ext
 def dont_use_config_in_tempest_lib(logical_line, filename):
     """Check that tempest.lib doesn't use tempest config
 
@@ -277,7 +275,8 @@
         yield(0, msg)
 
 
-def dont_put_admin_tests_on_nonadmin_path(logical_line, physical_line,
+@core.flake8ext
+def dont_put_admin_tests_on_nonadmin_path(logical_line,
                                           filename):
     """Check admin tests should exist under admin path
 
@@ -287,9 +286,6 @@
     if 'tempest/api/' not in filename:
         return
 
-    if pycodestyle.noqa(physical_line):
-        return
-
     if not re.match(r'class .*Test.*\(.*Admin.*\):', logical_line):
         return
 
@@ -298,6 +294,7 @@
         yield(0, msg)
 
 
+@core.flake8ext
 def unsupported_exception_attribute_PY3(logical_line):
     """Check Unsupported 'message' exception attribute in PY3
 
@@ -309,6 +306,7 @@
         yield(0, msg)
 
 
+@core.flake8ext
 def negative_test_attribute_always_applied_to_negative_tests(physical_line,
                                                              filename):
     """Check ``@decorators.attr(type=['negative'])`` applied to negative tests.
@@ -330,22 +328,3 @@
                        " to all negative API tests"
                 )
             _HAVE_NEGATIVE_DECORATOR = False
-
-
-def factory(register):
-    register(import_no_clients_in_api_and_scenario_tests)
-    register(scenario_tests_need_service_tags)
-    register(no_setup_teardown_class_for_tests)
-    register(no_vi_headers)
-    register(service_tags_not_in_module_path)
-    register(no_hyphen_at_end_of_rand_name)
-    register(no_mutable_default_args)
-    register(no_testtools_skip_decorator)
-    register(get_resources_on_service_clients)
-    register(delete_resources_on_service_clients)
-    register(dont_import_local_tempest_into_lib)
-    register(dont_use_config_in_tempest_lib)
-    register(use_rand_uuid_instead_of_uuid4)
-    register(dont_put_admin_tests_on_nonadmin_path)
-    register(unsupported_exception_attribute_PY3)
-    register(negative_test_attribute_always_applied_to_negative_tests)
diff --git a/tempest/lib/auth.py b/tempest/lib/auth.py
index 8e6d3d5..3fee489 100644
--- a/tempest/lib/auth.py
+++ b/tempest/lib/auth.py
@@ -684,7 +684,7 @@
 
     def __str__(self):
         """Represent only attributes included in self.ATTRIBUTES"""
-        attrs = [attr for attr in self.ATTRIBUTES if attr is not 'password']
+        attrs = [attr for attr in self.ATTRIBUTES if attr != 'password']
         _repr = dict((k, getattr(self, k)) for k in attrs)
         return str(_repr)
 
@@ -741,7 +741,7 @@
 
     def __str__(self):
         """Represent only attributes included in self.ATTRIBUTES"""
-        attrs = [attr for attr in self.ATTRIBUTES if attr is not 'password']
+        attrs = [attr for attr in self.ATTRIBUTES if attr != 'password']
         _repr = dict((k, getattr(self, k)) for k in attrs)
         return str(_repr)
 
diff --git a/tempest/tests/lib/services/object_storage/test_object_client.py b/tempest/tests/lib/services/object_storage/test_object_client.py
index a16d1d7..1749b03 100644
--- a/tempest/tests/lib/services/object_storage/test_object_client.py
+++ b/tempest/tests/lib/services/object_storage/test_object_client.py
@@ -69,7 +69,7 @@
 
         # If the expected initial status is not 100, then an exception
         # should be thrown and the connection closed
-        if initial_status is 100:
+        if initial_status == 100:
             status, reason = \
                 self.object_client.create_object_continue(cnt, obj, req_data)
         else:
@@ -91,7 +91,7 @@
         mock_poc.return_value.endheaders.assert_called_once_with()
 
         # The following steps are only taken if the initial status is 100
-        if initial_status is 100:
+        if initial_status == 100:
             # Verify that the method returned what it was supposed to
             self.assertEqual(status, 201)
 
diff --git a/tempest/tests/test_hacking.py b/tempest/tests/test_hacking.py
index 83c1abb..7c31185 100644
--- a/tempest/tests/test_hacking.py
+++ b/tempest/tests/test_hacking.py
@@ -101,17 +101,6 @@
             'def test_fake:', './tempest/scenario/orchestration/test_fake.py',
             "\n"))
 
-    def test_no_vi_headers(self):
-        # NOTE(mtreinish)  The lines parameter is used only for finding the
-        # line location in the file. So these tests just pass a list of an
-        # arbitrary length to use for verifying the check function.
-        self.assertTrue(checks.no_vi_headers(
-            '# vim: tabstop=4 shiftwidth=4 softtabstop=4', 1, range(250)))
-        self.assertTrue(checks.no_vi_headers(
-            '# vim: tabstop=4 shiftwidth=4 softtabstop=4', 249, range(250)))
-        self.assertFalse(checks.no_vi_headers(
-            '# vim: tabstop=4 shiftwidth=4 softtabstop=4', 149, range(250)))
-
     def test_service_tags_not_in_module_path(self):
         self.assertTrue(checks.service_tags_not_in_module_path(
             "@utils.services('compute')",
diff --git a/test-requirements.txt b/test-requirements.txt
index 196387c..a50905f 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -1,7 +1,7 @@
 # The order of packages is significant, because pip processes them in the order
 # of appearance. Changing the order has an impact on the overall integration
 # process, which may cause wedges in the gate later.
-hacking>=1.1.0,<1.2.0 # Apache-2.0
+hacking>=3.0,<3.1.0;python_version>='3.5' # Apache-2.0
 mock>=2.0.0 # BSD
 coverage!=4.4,>=4.0 # Apache-2.0
 oslotest>=3.2.0 # Apache-2.0
diff --git a/tox.ini b/tox.ini
index 64921ef..e861c84 100644
--- a/tox.ini
+++ b/tox.ini
@@ -313,7 +313,6 @@
     check-uuid --fix
 
 [hacking]
-local-check-factory = tempest.hacking.checks.factory
 import_exceptions = tempest.services
 
 [flake8]
@@ -327,6 +326,26 @@
 enable-extensions = H106,H203,H904
 import-order-style = pep8
 
+[flake8:local-plugins]
+extension =
+  T102 = checks:import_no_clients_in_api_and_scenario_tests
+  T104 = checks:scenario_tests_need_service_tags
+  T105 = checks:no_setup_teardown_class_for_tests
+  T107 = checks:service_tags_not_in_module_path
+  T108 = checks:no_hyphen_at_end_of_rand_name
+  N322 = checks:no_mutable_default_args
+  T109 = checks:no_testtools_skip_decorator
+  T110 = checks:get_resources_on_service_clients
+  T111 = checks:delete_resources_on_service_clients
+  T112 = checks:dont_import_local_tempest_into_lib
+  T113 = checks:use_rand_uuid_instead_of_uuid4
+  T114 = checks:dont_use_config_in_tempest_lib
+  T115 = checks:dont_put_admin_tests_on_nonadmin_path
+  T116 = checks:unsupported_exception_attribute_PY3
+  T117 = checks:negative_test_attribute_always_applied_to_negative_tests
+paths =
+  ./tempest/hacking
+
 [testenv:releasenotes]
 deps =
   -c{env:UPPER_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master}