Add hacking rules & fix hacking issues

Change-Id: Id335bfdf49ed88edd14e896a48c22b11113600c8
This commit is contained in:
Boris Pavlovic 2015-11-23 01:42:00 -08:00
parent cabf79d44c
commit 435e6a6fc3
14 changed files with 499 additions and 117 deletions

View File

@ -24,8 +24,8 @@ utils.import_modules_from_package("osprofiler._notifiers")
_conf = configparser.ConfigParser()
_conf.read(os.path.join(
os.path.dirname(os.path.dirname(__file__)), 'setup.cfg'))
os.path.dirname(os.path.dirname(__file__)), "setup.cfg"))
try:
__version__ = _conf.get('metadata', 'version')
__version__ = _conf.get("metadata", "version")
except (configparser.NoOptionError, configparser.NoSectionError):
__version__ = None

View File

@ -25,7 +25,7 @@ def env(*args, **kwargs):
value = os.environ.get(arg)
if value:
return value
return kwargs.get('default', '')
return kwargs.get("default", "")
def arg(*args, **kwargs):
@ -46,7 +46,7 @@ def arg(*args, **kwargs):
def add_arg(func, *args, **kwargs):
"""Bind CLI arguments to a shell.py `do_foo` function."""
if not hasattr(func, 'arguments'):
if not hasattr(func, "arguments"):
func.arguments = []
# NOTE(sirp): avoid dups that can occur when the module is shared across

View File

@ -28,12 +28,12 @@ class BaseCommand(object):
class TraceCommands(BaseCommand):
group_name = "trace"
@cliutils.arg('trace_id', help='trace id')
@cliutils.arg('--json', dest='use_json', action='store_true',
help='show trace in JSON')
@cliutils.arg('--html', dest='use_html', action='store_true',
help='show trace in HTML')
@cliutils.arg('--out', dest='file_name', help='save output in file')
@cliutils.arg("trace_id", help="trace id")
@cliutils.arg("--json", dest="use_json", action="store_true",
help="show trace in JSON")
@cliutils.arg("--html", dest="use_html", action="store_true",
help="show trace in HTML")
@cliutils.arg("--out", dest="file_name", help="save output in file")
def show(self, args):
"""Displays trace-results by given trace id in HTML or JSON format."""
try:
@ -50,7 +50,7 @@ class TraceCommands(BaseCommand):
notifications = ceiloparser.get_notifications(
client, args.trace_id)
except Exception as e:
if hasattr(e, 'http_status') and e.http_status == 401:
if hasattr(e, "http_status") and e.http_status == 401:
msg = "Invalid OpenStack Identity credentials."
else:
msg = "Something has gone wrong. See logs for more details."
@ -80,7 +80,7 @@ class TraceCommands(BaseCommand):
"output-formats: --json or --html.")
if args.file_name:
with open(args.file_name, 'w+') as output_file:
with open(args.file_name, "w+") as output_file:
output_file.write(output)
else:
print (output)

View File

@ -68,8 +68,8 @@ class OSProfilerShell(object):
add_help=True
)
parser.add_argument('-v', '--version',
action='version',
parser.add_argument("-v", "--version",
action="version",
version=osprofiler.__version__)
self._append_ceilometer_args(parser)
@ -79,24 +79,24 @@ class OSProfilerShell(object):
return parser
def _append_ceilometer_args(self, parent_parser):
parser = parent_parser.add_argument_group('ceilometer')
parser = parent_parser.add_argument_group("ceilometer")
parser.add_argument(
'--ceilometer-url', default=cliutils.env('CEILOMETER_URL'),
help='Defaults to env[CEILOMETER_URL].')
"--ceilometer-url", default=cliutils.env("CEILOMETER_URL"),
help="Defaults to env[CEILOMETER_URL].")
parser.add_argument(
'--ceilometer-api-version',
default=cliutils.env('CEILOMETER_API_VERSION', default='2'),
help='Defaults to env[CEILOMETER_API_VERSION] or 2.')
"--ceilometer-api-version",
default=cliutils.env("CEILOMETER_API_VERSION", default="2"),
help="Defaults to env[CEILOMETER_API_VERSION] or 2.")
def _append_identity_args(self, parent_parser):
# FIXME(fabgia): identity related parameters should be passed by the
# Keystone client itself to avoid constant update in all the services
# clients. When this fix is merged this method can be made obsolete.
# Bug: https://bugs.launchpad.net/python-keystoneclient/+bug/1332337
parser = parent_parser.add_argument_group('identity')
parser.add_argument('-k', '--insecure',
parser = parent_parser.add_argument_group("identity")
parser.add_argument("-k", "--insecure",
default=False,
action='store_true',
action="store_true",
help="Explicitly allow osprofiler to "
"perform \"insecure\" SSL (https) requests. "
"The server's certificate will "
@ -105,113 +105,113 @@ class OSProfilerShell(object):
"caution.")
# User related options
parser.add_argument('--os-username',
default=cliutils.env('OS_USERNAME'),
help='Defaults to env[OS_USERNAME].')
parser.add_argument("--os-username",
default=cliutils.env("OS_USERNAME"),
help="Defaults to env[OS_USERNAME].")
parser.add_argument('--os-user-id',
default=cliutils.env('OS_USER_ID'),
help='Defaults to env[OS_USER_ID].')
parser.add_argument("--os-user-id",
default=cliutils.env("OS_USER_ID"),
help="Defaults to env[OS_USER_ID].")
parser.add_argument('--os-password',
default=cliutils.env('OS_PASSWORD'),
help='Defaults to env[OS_PASSWORD].')
parser.add_argument("--os-password",
default=cliutils.env("OS_PASSWORD"),
help="Defaults to env[OS_PASSWORD].")
# Domain related options
parser.add_argument('--os-user-domain-id',
default=cliutils.env('OS_USER_DOMAIN_ID'),
help='Defaults to env[OS_USER_DOMAIN_ID].')
parser.add_argument("--os-user-domain-id",
default=cliutils.env("OS_USER_DOMAIN_ID"),
help="Defaults to env[OS_USER_DOMAIN_ID].")
parser.add_argument('--os-user-domain-name',
default=cliutils.env('OS_USER_DOMAIN_NAME'),
help='Defaults to env[OS_USER_DOMAIN_NAME].')
parser.add_argument("--os-user-domain-name",
default=cliutils.env("OS_USER_DOMAIN_NAME"),
help="Defaults to env[OS_USER_DOMAIN_NAME].")
parser.add_argument('--os-project-domain-id',
default=cliutils.env('OS_PROJECT_DOMAIN_ID'),
help='Defaults to env[OS_PROJECT_DOMAIN_ID].')
parser.add_argument("--os-project-domain-id",
default=cliutils.env("OS_PROJECT_DOMAIN_ID"),
help="Defaults to env[OS_PROJECT_DOMAIN_ID].")
parser.add_argument('--os-project-domain-name',
default=cliutils.env('OS_PROJECT_DOMAIN_NAME'),
help='Defaults to env[OS_PROJECT_DOMAIN_NAME].')
parser.add_argument("--os-project-domain-name",
default=cliutils.env("OS_PROJECT_DOMAIN_NAME"),
help="Defaults to env[OS_PROJECT_DOMAIN_NAME].")
# Project V3 or Tenant V2 related options
parser.add_argument('--os-project-id',
default=cliutils.env('OS_PROJECT_ID'),
help='Another way to specify tenant ID. '
'This option is mutually exclusive with '
' --os-tenant-id. '
'Defaults to env[OS_PROJECT_ID].')
parser.add_argument("--os-project-id",
default=cliutils.env("OS_PROJECT_ID"),
help="Another way to specify tenant ID. "
"This option is mutually exclusive with "
" --os-tenant-id. "
"Defaults to env[OS_PROJECT_ID].")
parser.add_argument('--os-project-name',
default=cliutils.env('OS_PROJECT_NAME'),
help='Another way to specify tenant name. '
'This option is mutually exclusive with '
' --os-tenant-name. '
'Defaults to env[OS_PROJECT_NAME].')
parser.add_argument("--os-project-name",
default=cliutils.env("OS_PROJECT_NAME"),
help="Another way to specify tenant name. "
"This option is mutually exclusive with "
" --os-tenant-name. "
"Defaults to env[OS_PROJECT_NAME].")
parser.add_argument('--os-tenant-id',
default=cliutils.env('OS_TENANT_ID'),
help='This option is mutually exclusive with '
' --os-project-id. '
'Defaults to env[OS_PROJECT_ID].')
parser.add_argument("--os-tenant-id",
default=cliutils.env("OS_TENANT_ID"),
help="This option is mutually exclusive with "
" --os-project-id. "
"Defaults to env[OS_PROJECT_ID].")
parser.add_argument('--os-tenant-name',
default=cliutils.env('OS_TENANT_NAME'),
help='Defaults to env[OS_TENANT_NAME].')
parser.add_argument("--os-tenant-name",
default=cliutils.env("OS_TENANT_NAME"),
help="Defaults to env[OS_TENANT_NAME].")
# Auth related options
parser.add_argument('--os-auth-url',
default=cliutils.env('OS_AUTH_URL'),
help='Defaults to env[OS_AUTH_URL].')
parser.add_argument("--os-auth-url",
default=cliutils.env("OS_AUTH_URL"),
help="Defaults to env[OS_AUTH_URL].")
parser.add_argument('--os-auth-token',
default=cliutils.env('OS_AUTH_TOKEN'),
help='Defaults to env[OS_AUTH_TOKEN].')
parser.add_argument("--os-auth-token",
default=cliutils.env("OS_AUTH_TOKEN"),
help="Defaults to env[OS_AUTH_TOKEN].")
parser.add_argument('--os-cacert',
metavar='<ca-certificate-file>',
dest='os_cacert',
default=cliutils.env('OS_CACERT'),
help='Path of CA TLS certificate(s) used to verify'
' the remote server\'s certificate. Without this '
'option ceilometer looks for the default system CA'
' certificates.')
parser.add_argument("--os-cacert",
metavar="<ca-certificate-file>",
dest="os_cacert",
default=cliutils.env("OS_CACERT"),
help="Path of CA TLS certificate(s) used to verify"
" the remote server\"s certificate. Without this "
"option ceilometer looks for the default system CA"
" certificates.")
parser.add_argument('--os-cert',
help='Path of certificate file to use in SSL '
'connection. This file can optionally be '
'prepended with the private key.')
parser.add_argument("--os-cert",
help="Path of certificate file to use in SSL "
"connection. This file can optionally be "
"prepended with the private key.")
parser.add_argument('--os-key',
help='Path of client key to use in SSL '
'connection. This option is not necessary '
'if your key is prepended to your cert file.')
parser.add_argument("--os-key",
help="Path of client key to use in SSL "
"connection. This option is not necessary "
"if your key is prepended to your cert file.")
# Service Catalog related options
parser.add_argument('--os-service-type',
default=cliutils.env('OS_SERVICE_TYPE'),
help='Defaults to env[OS_SERVICE_TYPE].')
parser.add_argument("--os-service-type",
default=cliutils.env("OS_SERVICE_TYPE"),
help="Defaults to env[OS_SERVICE_TYPE].")
parser.add_argument('--os-endpoint-type',
default=cliutils.env('OS_ENDPOINT_TYPE'),
help='Defaults to env[OS_ENDPOINT_TYPE].')
parser.add_argument("--os-endpoint-type",
default=cliutils.env("OS_ENDPOINT_TYPE"),
help="Defaults to env[OS_ENDPOINT_TYPE].")
parser.add_argument('--os-region-name',
default=cliutils.env('OS_REGION_NAME'),
help='Defaults to env[OS_REGION_NAME].')
parser.add_argument("--os-region-name",
default=cliutils.env("OS_REGION_NAME"),
help="Defaults to env[OS_REGION_NAME].")
def _append_subcommands(self, parent_parser):
subcommands = parent_parser.add_subparsers(help='<subcommands>')
subcommands = parent_parser.add_subparsers(help="<subcommands>")
for group_cls in commands.BaseCommand.__subclasses__():
group_parser = subcommands.add_parser(group_cls.group_name)
subcommand_parser = group_parser.add_subparsers()
for name, callback in inspect.getmembers(
group_cls(), predicate=inspect.ismethod):
command = name.replace('_', '-')
desc = callback.__doc__ or ''
help_message = desc.strip().split('\n')[0]
arguments = getattr(callback, 'arguments', [])
command = name.replace("_", "-")
desc = callback.__doc__ or ""
help_message = desc.strip().split("\n")[0]
arguments = getattr(callback, "arguments", [])
command_parser = subcommand_parser.add_parser(
command, help=help_message, description=desc)

View File

@ -55,6 +55,6 @@ def create(plugin_name, *args, **kwargs):
:param *args: args that will be passed to plugin init method
:param **kwargs: kwargs that will be passed to plugin init method
:returns: Callable notifier method
:raise TypeError: In case of invalid name of plugin raises TypeError
:raises TypeError: In case of invalid name of plugin raises TypeError
"""
return base.Notifier.factory(plugin_name, *args, **kwargs)

View File

@ -126,6 +126,6 @@ def get_notifications(ceilometer, base_id):
:param base_id: Base id of trace elements.
"""
_filter = '{"=": {"resource_id": "profiler-%s"}}' % base_id
_filter = "{\"=\": {\"resource_id\": \"profiler-%s\"}}" % base_id
return [n.to_dict()
for n in ceilometer.query_samples.query(_filter, None, None)]

View File

@ -243,7 +243,7 @@ class _Profiler(object):
:param info: Dict with useful info. It will be send in notification.
"""
self._notify('%s-stop' % self._name.pop(), info)
self._notify("%s-stop" % self._name.pop(), info)
self._trace_stack.pop()
def _notify(self, name, info):

View File

@ -36,9 +36,9 @@ def add_tracing(sqlalchemy, engine, name):
"""Add tracing to all sqlalchemy calls."""
if not _DISABLED:
sqlalchemy.event.listen(engine, 'before_cursor_execute',
sqlalchemy.event.listen(engine, "before_cursor_execute",
_before_cursor_execute(name))
sqlalchemy.event.listen(engine, 'after_cursor_execute',
sqlalchemy.event.listen(engine, "after_cursor_execute",
_after_cursor_execute())

View File

378
tests/hacking/checks.py Normal file
View File

@ -0,0 +1,378 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
Guidelines for writing new hacking checks
- Use only for OSProfiler specific tests. OpenStack general tests
should be submitted to the common 'hacking' module.
- Pick numbers in the range N3xx. Find the current test with
the highest allocated number and then pick the next value.
- Keep the test method code in the source file ordered based
on the N3xx value.
- List the new rule in the top level HACKING.rst file
- Add test cases for each new rule to tests/unit/test_hacking.py
"""
import functools
import re
import tokenize
re_assert_true_instance = re.compile(
r"(.)*assertTrue\(isinstance\((\w|\.|\'|\"|\[|\])+, "
r"(\w|\.|\'|\"|\[|\])+\)\)")
re_assert_equal_type = re.compile(
r"(.)*assertEqual\(type\((\w|\.|\'|\"|\[|\])+\), "
r"(\w|\.|\'|\"|\[|\])+\)")
re_assert_equal_end_with_none = re.compile(r"assertEqual\(.*?,\s+None\)$")
re_assert_equal_start_with_none = re.compile(r"assertEqual\(None,")
re_assert_true_false_with_in_or_not_in = re.compile(
r"assert(True|False)\("
r"(\w|[][.'\"])+( not)? in (\w|[][.'\",])+(, .*)?\)")
re_assert_true_false_with_in_or_not_in_spaces = re.compile(
r"assert(True|False)\((\w|[][.'\"])+( not)? in [\[|'|\"](\w|[][.'\", ])+"
r"[\[|'|\"](, .*)?\)")
re_assert_equal_in_end_with_true_or_false = re.compile(
r"assertEqual\((\w|[][.'\"])+( not)? in (\w|[][.'\", ])+, (True|False)\)")
re_assert_equal_in_start_with_true_or_false = re.compile(
r"assertEqual\((True|False), (\w|[][.'\"])+( not)? in (\w|[][.'\", ])+\)")
re_no_construct_dict = re.compile(
r"\sdict\(\)")
re_no_construct_list = re.compile(
r"\slist\(\)")
re_str_format = re.compile(r"""
% # start of specifier
\(([^)]+)\) # mapping key, in group 1
[#0 +\-]? # optional conversion flag
(?:-?\d*)? # optional minimum field width
(?:\.\d*)? # optional precision
[hLl]? # optional length modifier
[A-z%] # conversion modifier
""", re.X)
re_raises = re.compile(
r"\s:raise[^s] *.*$|\s:raises *:.*$|\s:raises *[^:]+$")
def skip_ignored_lines(func):
@functools.wraps(func)
def wrapper(logical_line, filename):
line = logical_line.strip()
if not line or line.startswith("#") or line.endswith("# noqa"):
return
yield next(func(logical_line, filename))
return wrapper
def _parse_assert_mock_str(line):
point = line.find(".assert_")
if point != -1:
end_pos = line[point:].find("(") + point
return point, line[point + 1: end_pos], line[: point]
else:
return None, None, None
@skip_ignored_lines
def check_assert_methods_from_mock(logical_line, filename):
"""Ensure that ``assert_*`` methods from ``mock`` library is used correctly
N301 - base error number
N302 - related to nonexistent "assert_called"
N303 - related to nonexistent "assert_called_once"
"""
correct_names = ["assert_any_call", "assert_called_once_with",
"assert_called_with", "assert_has_calls"]
ignored_files = ["./tests/unit/test_hacking.py"]
if filename.startswith("./tests") and filename not in ignored_files:
pos, method_name, obj_name = _parse_assert_mock_str(logical_line)
if pos:
if method_name not in correct_names:
error_number = "N301"
msg = ("%(error_number)s:'%(method)s' is not present in `mock`"
" library. %(custom_msg)s For more details, visit "
"http://www.voidspace.org.uk/python/mock/ .")
if method_name == "assert_called":
error_number = "N302"
custom_msg = ("Maybe, you should try to use "
"'assertTrue(%s.called)' instead." %
obj_name)
elif method_name == "assert_called_once":
# For more details, see a bug in Rally:
# https://bugs.launchpad.net/rally/+bug/1305991
error_number = "N303"
custom_msg = ("Maybe, you should try to use "
"'assertEqual(1, %s.call_count)' "
"or '%s.assert_called_once_with()'"
" instead." % (obj_name, obj_name))
else:
custom_msg = ("Correct 'assert_*' methods: '%s'."
% "', '".join(correct_names))
yield (pos, msg % {
"error_number": error_number,
"method": method_name,
"custom_msg": custom_msg})
@skip_ignored_lines
def assert_true_instance(logical_line, filename):
"""Check for assertTrue(isinstance(a, b)) sentences
N320
"""
if re_assert_true_instance.match(logical_line):
yield (0, "N320 assertTrue(isinstance(a, b)) sentences not allowed, "
"you should use assertIsInstance(a, b) instead.")
@skip_ignored_lines
def assert_equal_type(logical_line, filename):
"""Check for assertEqual(type(A), B) sentences
N321
"""
if re_assert_equal_type.match(logical_line):
yield (0, "N321 assertEqual(type(A), B) sentences not allowed, "
"you should use assertIsInstance(a, b) instead.")
@skip_ignored_lines
def assert_equal_none(logical_line, filename):
"""Check for assertEqual(A, None) or assertEqual(None, A) sentences
N322
"""
res = (re_assert_equal_start_with_none.search(logical_line) or
re_assert_equal_end_with_none.search(logical_line))
if res:
yield (0, "N322 assertEqual(A, None) or assertEqual(None, A) "
"sentences not allowed, you should use assertIsNone(A) "
"instead.")
@skip_ignored_lines
def assert_true_or_false_with_in(logical_line, filename):
"""Check assertTrue/False(A in/not in B) with collection contents
Check for assertTrue/False(A in B), assertTrue/False(A not in B),
assertTrue/False(A in B, message) or assertTrue/False(A not in B, message)
sentences.
N323
"""
res = (re_assert_true_false_with_in_or_not_in.search(logical_line) or
re_assert_true_false_with_in_or_not_in_spaces.search(logical_line))
if res:
yield (0, "N323 assertTrue/assertFalse(A in/not in B)sentences not "
"allowed, you should use assertIn(A, B) or assertNotIn(A, B)"
" instead.")
@skip_ignored_lines
def assert_equal_in(logical_line, filename):
"""Check assertEqual(A in/not in B, True/False) with collection contents
Check for assertEqual(A in B, True/False), assertEqual(True/False, A in B),
assertEqual(A not in B, True/False) or assertEqual(True/False, A not in B)
sentences.
N324
"""
res = (re_assert_equal_in_end_with_true_or_false.search(logical_line) or
re_assert_equal_in_start_with_true_or_false.search(logical_line))
if res:
yield (0, "N324: Use assertIn/NotIn(A, B) rather than "
"assertEqual(A in/not in B, True/False) when checking "
"collection contents.")
@skip_ignored_lines
def check_quotes(logical_line, filename):
"""Check that single quotation marks are not used
N350
"""
in_string = False
in_multiline_string = False
single_quotas_are_used = False
check_tripple = (
lambda line, i, char: (
i + 2 < len(line) and
(char == line[i] == line[i + 1] == line[i + 2])
)
)
i = 0
while i < len(logical_line):
char = logical_line[i]
if in_string:
if char == "\"":
in_string = False
if char == "\\":
i += 1 # ignore next char
elif in_multiline_string:
if check_tripple(logical_line, i, "\""):
i += 2 # skip next 2 chars
in_multiline_string = False
elif char == "#":
break
elif char == "'":
single_quotas_are_used = True
break
elif char == "\"":
if check_tripple(logical_line, i, "\""):
in_multiline_string = True
i += 3
continue
in_string = True
i += 1
if single_quotas_are_used:
yield (i, "N350 Remove Single quotes")
@skip_ignored_lines
def check_no_constructor_data_struct(logical_line, filename):
"""Check that data structs (lists, dicts) are declared using literals
N351
"""
match = re_no_construct_dict.search(logical_line)
if match:
yield (0, "N351 Remove dict() construct and use literal {}")
match = re_no_construct_list.search(logical_line)
if match:
yield (0, "N351 Remove list() construct and use literal []")
def check_dict_formatting_in_string(logical_line, tokens):
"""Check that strings do not use dict-formatting with a single replacement
N352
"""
# NOTE(stpierre): Can't use @skip_ignored_lines here because it's
# a stupid decorator that only works on functions that take
# (logical_line, filename) as arguments.
if (not logical_line or
logical_line.startswith("#") or
logical_line.endswith("# noqa")):
return
current_string = ""
in_string = False
for token_type, text, start, end, line in tokens:
if token_type == tokenize.STRING:
if not in_string:
current_string = ""
in_string = True
current_string += text.strip("\"")
elif token_type == tokenize.OP:
if not current_string:
continue
# NOTE(stpierre): The string formatting operator % has
# lower precedence than +, so we assume that the logical
# string has concluded whenever we hit an operator of any
# sort. (Most operators don't work for strings anyway.)
# Some string operators do have higher precedence than %,
# though, so you can technically trick this check by doing
# things like:
#
# "%(foo)s" * 1 % {"foo": 1}
# "%(foo)s"[:] % {"foo": 1}
#
# It also will produce false positives if you use explicit
# parenthesized addition for two strings instead of
# concatenation by juxtaposition, e.g.:
#
# ("%(foo)s" + "%(bar)s") % vals
#
# But if you do any of those things, then you deserve all
# of the horrible things that happen to you, and probably
# many more.
in_string = False
if text == "%":
format_keys = set()
for match in re_str_format.finditer(current_string):
format_keys.add(match.group(1))
if len(format_keys) == 1:
yield (0,
"N353 Do not use mapping key string formatting "
"with a single key")
if text != ")":
# NOTE(stpierre): You can have a parenthesized string
# followed by %, so a closing paren doesn't obviate
# the possibility for a substitution operator like
# every other operator does.
current_string = ""
elif token_type in (tokenize.NL, tokenize.COMMENT):
continue
else:
in_string = False
if token_type == tokenize.NEWLINE:
current_string = ""
@skip_ignored_lines
def check_using_unicode(logical_line, filename):
"""Check crosspython unicode usage
N353
"""
if re.search(r"\bunicode\(", logical_line):
yield (0, "N353 'unicode' function is absent in python3. Please "
"use 'six.text_type' instead.")
def check_raises(physical_line, filename):
"""Check raises usage
N354
"""
ignored_files = ["./tests/unit/test_hacking.py",
"./tests/hacking/checks.py"]
if filename not in ignored_files:
if re_raises.search(physical_line):
return (0, "N354 ':Please use ':raises Exception: conditions' "
"in docstrings.")
def factory(register):
register(check_assert_methods_from_mock)
register(assert_true_instance)
register(assert_equal_type)
register(assert_equal_none)
register(assert_true_or_false_with_in)
register(assert_equal_in)
register(check_quotes)
register(check_no_constructor_data_struct)
register(check_dict_formatting_in_string)
register(check_using_unicode)
register(check_raises)

View File

@ -247,7 +247,8 @@ class CeilometerParserTestCase(test.TestCase):
result = ceilometer.get_notifications(mock_ceil_client, base_id)
expected_filter = '{"=": {"resource_id": "profiler-%s"}}' % base_id
expected_filter = (
"{\"=\": {\"resource_id\": \"profiler-%s\"}}" % base_id)
mock_ceil_client.query_samples.query.assert_called_once_with(
expected_filter, None, None)
self.assertEqual(result, [results[0].to_dict(), results[1].to_dict()])

View File

@ -67,21 +67,21 @@ class UtilsTestCase(test.TestCase):
process_data = utils.signed_unpack(packed_data, hmac_data, [hmac])
self.assertIn("hmac_key", process_data)
process_data.pop('hmac_key')
process_data.pop("hmac_key")
self.assertEqual(data, process_data)
def test_signed_pack_unpack_many_keys(self):
keys = ['secret', 'secret2', 'secret3']
keys = ["secret", "secret2", "secret3"]
data = {"some": "data"}
packed_data, hmac_data = utils.signed_pack(data, keys[-1])
process_data = utils.signed_unpack(packed_data, hmac_data, keys)
self.assertEqual(keys[-1], process_data['hmac_key'])
self.assertEqual(keys[-1], process_data["hmac_key"])
def test_signed_pack_unpack_many_wrong_keys(self):
keys = ['secret', 'secret2', 'secret3']
keys = ["secret", "secret2", "secret3"]
data = {"some": "data"}
packed_data, hmac_data = utils.signed_pack(data, 'password')
packed_data, hmac_data = utils.signed_pack(data, "password")
process_data = utils.signed_unpack(packed_data, hmac_data, keys)
self.assertIsNone(process_data)

View File

@ -48,9 +48,9 @@ class WebTestCase(test.TestCase):
trace_info = utils.signed_unpack(headers["X-Trace-Info"],
headers["X-Trace-HMAC"], ["key"])
self.assertIn('hmac_key', trace_info)
self.assertEqual('key', trace_info.pop('hmac_key'))
self.assertEqual({"parent_id": 'z', 'base_id': 'y'}, trace_info)
self.assertIn("hmac_key", trace_info)
self.assertEqual("key", trace_info.pop("hmac_key"))
self.assertEqual({"parent_id": "z", "base_id": "y"}, trace_info)
@mock.patch("osprofiler.profiler.get")
def test_get_trace_id_headers_no_profiler(self, mock_get_profiler):
@ -280,7 +280,7 @@ class WebMiddlewareTestCase(test.TestCase):
request.query_string = "query"
request.method = "method"
request.scheme = "scheme"
hmac_key = 'super_secret_key2'
hmac_key = "super_secret_key2"
pack = utils.signed_pack({"base_id": "1", "parent_id": "2"}, hmac_key)
request.headers = {
@ -290,7 +290,7 @@ class WebMiddlewareTestCase(test.TestCase):
"X-Trace-HMAC": pack[1]
}
web.enable('super_secret_key1,super_secret_key2')
web.enable("super_secret_key1,super_secret_key2")
middleware = web.WsgiMiddleware("app", enabled=True)
self.assertEqual("yeah!", middleware(request))
mock_profiler_init.assert_called_once_with(hmac_key=hmac_key,

View File

@ -36,4 +36,7 @@ downloadcache = ~/cache/pip
ignore = E126,H703
show-source = true
builtins = _
exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,tools
exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,tools,setup.py
[hacking]
local-check-factory = tests.hacking.checks.factory