Switch to hacking 2.0

In hacking 2.0, local-check-factory was removed as it is not compatible
with flake8 3.x and it is advised to use flake8's local plugins [1].
Checks specific to neutron-lib registered via local-check-factory are
converted into flake8's local plugins [2].

Note that neutron_lib.hacking.checks.factory is kept not to break hacking
checks in neutron-lib consumers. They need to be converted into the style
in hacking 2.x in each repository and then we can drop the factory here.

[1] https://docs.openstack.org/releasenotes/hacking/unreleased.html#relnotes-2-0-0
[2] https://flake8.pycqa.org/en/3.7.0/user/configuration.html#using-local-plugins

Change-Id: I90419fe0b385e7bee216a52c1169aba6d6975d56
This commit is contained in:
Akihiro Motoki 2020-02-21 06:05:59 +09:00
parent c099a22789
commit f4f5f1c719
7 changed files with 93 additions and 55 deletions

View File

@ -3,35 +3,43 @@ Hacking Checks
============== ==============
The ``neutron_lib.hacking`` package implements a number of public The ``neutron_lib.hacking`` package implements a number of public
`hacking checks <https://github.com/openstack-dev/hacking>`_ intended to help `flake8 checks <https://gitlab.com/pycqa/flake8>`__ intended to help
adopters validate their compliance with the latest hacking standards. adopters validate their compliance with the latest hacking standards.
To adopt neutron-lib's hacking checks: To adopt neutron-lib's hacking checks:
#. Update your project's ``tox.ini`` to use #. Update your project's ``tox.ini`` to include hacking checks from
``neutron_lib.hacking.checks.factory`` for its ``local-check-factory``. neutron-lib. More specifically, copy hacking checks under
"Checks for neutron and related projects" in
``[flake8.local-plugin] extension`` in neutron-lib ``tox.ini``
to ``[flake8.local-plugin] extension`` in your project's ``tox.ini``.
For example in your ``tox.ini``:: For example in your ``tox.ini``::
[hacking] [flake8:local-plugins]
local-check-factory = neutron_lib.hacking.checks.factory extension =
# Checks from neutron-lib
N521 = neutron_lib.hacking.checks:use_jsonutils
N524 = neutron_lib.hacking.checks:check_no_contextlib_nested
N529 = neutron_lib.hacking.checks:no_mutable_default_args
N530 = neutron_lib.hacking.checks:check_neutron_namespace_imports
N532 = neutron_lib.hacking.translation_checks:check_log_warn_deprecated
N534 = neutron_lib.hacking.translation_checks:check_raised_localized_exceptions
N536 = neutron_lib.hacking.checks:assert_equal_none
N537 = neutron_lib.hacking.translation_checks:no_translate_logs
If your project needs to register additional project specific hacking Under certain circumstances, adopters may need to ignore specific
checks, you can define your own factory function that calls neutron-lib's neutron-lib hacking checks temporarily. You can ignore such checks
``factory`` function. just by commenting out them (hopefully with a proper reason).
For example in your project's python source:: If your project has its own hacking checks, you can add more rules
to ``[flake8.local-plugin] extension`` along with hacking checks
from neutron-lib.
def my_factory(register): .. note::
# register neutron-lib checks
neutron_lib_checks.factory(register)
# register project specific checks
register(my_project_check)
And use your project's own factory in ``tox.ini``:: The above configuration assumes hacking 2.x.
If your project uses hacking 1.x, see :ref:`hacking1_support` below.
[hacking]
local-check-factory = myproject.mypkg.my_factory
#. Update your project's ``tox.ini`` enable any flake8 extensions neutron-lib's #. Update your project's ``tox.ini`` enable any flake8 extensions neutron-lib's
``tox.ini`` does. These are hacking checks otherwise disabled by default ``tox.ini`` does. These are hacking checks otherwise disabled by default
@ -54,12 +62,40 @@ To adopt neutron-lib's hacking checks:
hacking checks will be communicated via openstack-discuss email list hacking checks will be communicated via openstack-discuss email list
and `neutron meetings <https://wiki.openstack.org/wiki/Network/Meetings>`_. and `neutron meetings <https://wiki.openstack.org/wiki/Network/Meetings>`_.
Under certain circumstances, adopters may need to ignore specific .. _hacking1_support:
neutron-lib hacking checks temporarily. This can be done using the
``ignore`` property in the ``[flake8]`` section of your ``tox.ini``.
For example, to ignore the hacking check ``N536`` your tox.ini might
have::
[flake8] Hacking 1.x support
# temporarily ignore N536 while fixing failing checks -------------------
ignore = N536
If your project uses hacking 1.x, you need a different way to consume hacking
checks from neutron-lib.
.. warning::
hacking 1.x support is deprecated and will be dropped once all neutron
related projects migrate to hacking 2.x.
Update your project's ``tox.ini`` to use
``neutron_lib.hacking.checks.factory`` for its ``local-check-factory``.
For example in your ``tox.ini``::
[hacking]
local-check-factory = neutron_lib.hacking.checks.factory
If your project needs to register additional project specific hacking
checks, you can define your own factory function that calls neutron-lib's
``factory`` function.
For example in your project's python source::
def my_factory(register):
# register neutron-lib checks
neutron_lib_checks.factory(register)
# register project specific checks
register(my_project_check)
And use your project's own factory in ``tox.ini``::
[hacking]
local-check-factory = myproject.mypkg.my_factory

View File

@ -14,6 +14,8 @@
import re import re
from hacking import core
from neutron_lib.hacking import translation_checks from neutron_lib.hacking import translation_checks
# Guidelines for writing new hacking checks # Guidelines for writing new hacking checks
@ -41,6 +43,7 @@ assert_is_none_re = re.compile(
r"assertIs(Not)?\(.*,\s+None\)(( |\t)*#.*)?$|assertIs(Not)?\(None,") r"assertIs(Not)?\(.*,\s+None\)(( |\t)*#.*)?$|assertIs(Not)?\(None,")
@core.flake8ext
def use_jsonutils(logical_line, filename): def use_jsonutils(logical_line, filename):
"""N521 - jsonutils must be used instead of json. """N521 - jsonutils must be used instead of json.
@ -105,6 +108,7 @@ def _check_namespace_imports(failure_code, namespace, new_ns, logical_line,
return (0, msg_o or msg) return (0, msg_o or msg)
@core.flake8ext
def check_no_contextlib_nested(logical_line, filename): def check_no_contextlib_nested(logical_line, filename):
"""N524 - Use of contextlib.nested is deprecated. """N524 - Use of contextlib.nested is deprecated.
@ -123,6 +127,7 @@ def check_no_contextlib_nested(logical_line, filename):
yield(0, msg) yield(0, msg)
@core.flake8ext
def no_mutable_default_args(logical_line): def no_mutable_default_args(logical_line):
"""N529 - Method's default argument shouldn't be mutable. """N529 - Method's default argument shouldn't be mutable.
@ -139,6 +144,7 @@ def no_mutable_default_args(logical_line):
# Chances are that most projects will need to put an ignore on this rule # Chances are that most projects will need to put an ignore on this rule
# until they can fully migrate to the lib. # until they can fully migrate to the lib.
@core.flake8ext
def check_neutron_namespace_imports(logical_line): def check_neutron_namespace_imports(logical_line):
"""N530 - Direct neutron imports not allowed. """N530 - Direct neutron imports not allowed.
@ -154,6 +160,7 @@ def check_neutron_namespace_imports(logical_line):
yield x yield x
@core.flake8ext
def check_no_eventlet_imports(logical_line): def check_no_eventlet_imports(logical_line):
"""N535 - Usage of Python eventlet module not allowed. """N535 - Usage of Python eventlet module not allowed.
@ -167,6 +174,7 @@ def check_no_eventlet_imports(logical_line):
yield logical_line.index('eventlet'), msg yield logical_line.index('eventlet'), msg
@core.flake8ext
def assert_equal_none(logical_line): def assert_equal_none(logical_line):
"""N536 - Use assertIsNone.""" """N536 - Use assertIsNone."""
if assert_equal_none_re.search(logical_line): if assert_equal_none_re.search(logical_line):
@ -180,6 +188,8 @@ def assert_equal_none(logical_line):
yield logical_line.index('assert'), msg yield logical_line.index('assert'), msg
# TODO(amotoki): Drop this once all neutron related projects
# have switched to hacking 2.x
def factory(register): def factory(register):
"""Hacking check factory for neutron-lib adopter compliant checks. """Hacking check factory for neutron-lib adopter compliant checks.
@ -197,18 +207,3 @@ def factory(register):
register(translation_checks.check_log_warn_deprecated) register(translation_checks.check_log_warn_deprecated)
register(translation_checks.check_raised_localized_exceptions) register(translation_checks.check_raised_localized_exceptions)
register(assert_equal_none) register(assert_equal_none)
def _neutron_lib_factory(register):
"""Hacking check factory for neutron-lib internal project checks.
Hacking check factory for use with tox.ini. This factory registers all
checks that are run with the neutron-lib project itself.
:param register: The function to register the check functions with.
:returns: None.
"""
factory(register)
# neutron-lib project specific checks below
register(check_no_eventlet_imports)

View File

@ -14,6 +14,8 @@
import re import re
from hacking import core
_all_log_levels = {'critical', 'error', 'exception', 'info', _all_log_levels = {'critical', 'error', 'exception', 'info',
'warning', 'debug'} 'warning', 'debug'}
@ -36,6 +38,7 @@ def _translation_checks_not_enforced(filename):
return any(pat in filename for pat in ["/tests/", "rally-jobs/plugins/"]) return any(pat in filename for pat in ["/tests/", "rally-jobs/plugins/"])
@core.flake8ext
def check_log_warn_deprecated(logical_line, filename): def check_log_warn_deprecated(logical_line, filename):
"""N532 - Use LOG.warning due to compatibility with py3. """N532 - Use LOG.warning due to compatibility with py3.
@ -50,6 +53,7 @@ def check_log_warn_deprecated(logical_line, filename):
yield (0, msg) yield (0, msg)
@core.flake8ext
def check_raised_localized_exceptions(logical_line, filename): def check_raised_localized_exceptions(logical_line, filename):
"""N534 - Untranslated exception message. """N534 - Untranslated exception message.
@ -72,6 +76,7 @@ def check_raised_localized_exceptions(logical_line, filename):
yield (logical_line.index(exception_msg), msg) yield (logical_line.index(exception_msg), msg)
@core.flake8ext
def no_translate_logs(logical_line, filename): def no_translate_logs(logical_line, filename):
"""N537 - Don't translate logs. """N537 - Don't translate logs.

View File

@ -117,7 +117,7 @@ class NoAuthClient(object):
LOG.exception('requests Timeout, let\'s retry it...') LOG.exception('requests Timeout, let\'s retry it...')
except requests.ConnectionError: except requests.ConnectionError:
LOG.exception('Connection Error appeared') LOG.exception('Connection Error appeared')
except requests.RequestException as e: except requests.RequestException:
LOG.exception('Some really weird thing happened, let\'s ' LOG.exception('Some really weird thing happened, let\'s '
'retry it') 'retry it')
time.sleep(self.timeout) time.sleep(self.timeout)

View File

@ -40,17 +40,6 @@ class HackingTestCase(base.BaseTestCase):
def test_factory(self): def test_factory(self):
self.assertGreater(len(self._get_factory_checks(checks.factory)), 0) self.assertGreater(len(self._get_factory_checks(checks.factory)), 0)
def test_neutron_lib_factory(self):
lib_checks = self._get_factory_checks(checks._neutron_lib_factory)
other_checks = self._get_factory_checks(checks.factory)
self.assertGreater(len(lib_checks), 0)
if other_checks:
for other_check in other_checks:
# lib checks are superset of all checks
self.assertTrue(other_check in lib_checks)
def test_use_jsonutils(self): def test_use_jsonutils(self):
def __get_msg(fun): def __get_msg(fun):
msg = ("N521: jsonutils.%(fun)s must be used instead of " msg = ("N521: jsonutils.%(fun)s must be used instead of "

View File

@ -2,7 +2,7 @@
# of appearance. Changing the order has an impact on the overall integration # of appearance. Changing the order has an impact on the overall integration
# process, which may cause wedges in the gate later. # process, which may cause wedges in the gate later.
hacking>=1.1.0,<1.2.0 # Apache-2.0 hacking>=2.0.0,<2.1 # Apache-2.0
bandit!=1.6.0,>=1.1.0 # Apache-2.0 bandit!=1.6.0,>=1.1.0 # Apache-2.0
coverage!=4.4,>=4.0 # Apache-2.0 coverage!=4.4,>=4.0 # Apache-2.0

15
tox.ini
View File

@ -101,6 +101,20 @@ show-source = True
exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,build,tools exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,build,tools
import-order-style = pep8 import-order-style = pep8
[flake8:local-plugins]
extension =
# Checks for neutron and related projects
N521 = neutron_lib.hacking.checks:use_jsonutils
N524 = neutron_lib.hacking.checks:check_no_contextlib_nested
N529 = neutron_lib.hacking.checks:no_mutable_default_args
N530 = neutron_lib.hacking.checks:check_neutron_namespace_imports
N532 = neutron_lib.hacking.translation_checks:check_log_warn_deprecated
N534 = neutron_lib.hacking.translation_checks:check_raised_localized_exceptions
N536 = neutron_lib.hacking.checks:assert_equal_none
N537 = neutron_lib.hacking.translation_checks:no_translate_logs
# Checks specific to neutron-lib only
N535 = neutron_lib.hacking.checks:check_no_eventlet_imports
[testenv:bandit] [testenv:bandit]
# B104: Possible binding to all interfaces # B104: Possible binding to all interfaces
# B303: Blacklist use of insecure MD2, MD4, MD5, or SHA1 hash functions # B303: Blacklist use of insecure MD2, MD4, MD5, or SHA1 hash functions
@ -110,7 +124,6 @@ commands = bandit -r neutron_lib -x tests -n5 -s B104,B303,B311
[hacking] [hacking]
import_exceptions = neutron_lib._i18n import_exceptions = neutron_lib._i18n
local-check-factory = neutron_lib.hacking.checks._neutron_lib_factory
[testenv:lower-constraints] [testenv:lower-constraints]
deps = deps =