Added basic Python 2.6 support (#43)

pull/44/head^2
Andrew Krasichkov 2017-05-16 23:02:10 +03:00 committed by GitHub
parent 938889961a
commit 39fa26c7fe
19 changed files with 125 additions and 84 deletions

View File

@ -1,6 +1,7 @@
language: python
sudo: false
python:
- "2.6"
- "2.7"
- "3.5"
- "3.6"
@ -11,4 +12,4 @@ install:
script:
- nosetests --with-coverage --cover-package gixy -v
- flake8 --max-line-length=120 setup.py yodax
- if [[ $TRAVIS_PYTHON_VERSION != '2.6' ]]; then flake8 --max-line-length=120 setup.py gixy; fi

View File

@ -92,7 +92,7 @@ class ArgsParser(ArgumentParser):
"""
keys = []
for arg in action.option_strings:
if arg in {'--config', '--write-config', '--version'}:
if arg in ['--config', '--write-config', '--version']:
continue
if any([arg.startswith(2 * c) for c in self.prefix_chars]):
keys += [arg[2:], arg] # eg. for '--bla' return ['bla', '--bla']

View File

@ -16,7 +16,6 @@ LOG = logging.getLogger()
def _init_logger(debug=False):
LOG.handlers = []
log_level = logging.DEBUG if debug else logging.INFO
logging.captureWarnings(True)
LOG.setLevel(log_level)
handler = logging.StreamHandler(sys.stderr)
@ -31,7 +30,7 @@ def _create_plugin_help(option):
else:
default = str(option)
return 'Default: {}'.format(default)
return 'Default: {0}'.format(default)
def _get_cli_parser():
@ -41,7 +40,7 @@ def _get_cli_parser():
parser.add_argument(
'-v', '--version', action='version',
version='Gixy v{}'.format(gixy.version))
version='Gixy v{0}'.format(gixy.version))
parser.add_argument(
'-l', '--level', dest='level', action='count', default=0,
@ -106,7 +105,7 @@ def main():
try:
severity = gixy.severity.ALL[args.level]
except IndexError:
sys.stderr.write('Too high level filtering. Maximum level: -{}\n'.format('l' * (len(gixy.severity.ALL) - 1)))
sys.stderr.write('Too high level filtering. Maximum level: -{0}\n'.format('l' * (len(gixy.severity.ALL) - 1)))
sys.exit(1)
if args.tests:
@ -132,7 +131,7 @@ def main():
name = plugin_cls.__name__
options = copy.deepcopy(plugin_cls.options)
for opt_key, opt_val in options.items():
option_name = '{}:{}'.format(name, opt_key)
option_name = '{name}:{key}'.format(name=name, key=opt_key)
if option_name not in args:
continue

View File

@ -75,7 +75,7 @@ class Context(object):
result = builtins.builtin_var(name)
if not result:
LOG.info("Can't find variable '{}'".format(name))
LOG.info("Can't find variable '{0}'".format(name))
return result
def __deepcopy__(self, memo):

View File

@ -432,7 +432,7 @@ class BranchToken(Token):
elif isinstance(token, sre_parse.SubPattern):
self.childs.append(InternalSubpatternToken(token=token, parent=self.parent, regexp=self.regexp))
else:
raise RuntimeError('Unexpected token {} in branch'.format(token))
raise RuntimeError('Unexpected token {0} in branch'.format(token))
def can_contain(self, char, skip_literal=True):
for child in self.childs:
@ -461,7 +461,7 @@ class BranchToken(Token):
return res
def __str__(self):
return '(?:{})'.format('|'.join(str(x) for x in self.childs))
return '(?:{0})'.format('|'.join(str(x) for x in self.childs))
class SubpatternToken(Token):
@ -661,7 +661,7 @@ class InToken(Token):
elif isinstance(child, CategoryToken):
blacklisted.update(child.char_list)
else:
LOG.info('Unexpected child "{!r}"'.format(child))
LOG.info('Unexpected child "{0!r}"'.format(child))
for char in _build_reverse_list(set()):
if char not in blacklisted:
@ -756,7 +756,7 @@ class GroupRefToken(Token):
return self.group.generate(context)
def __str__(self):
return '\\\\{}'.format(self.id)
return '\\\\{0}'.format(self.id)
class AssertToken(Token):
@ -854,7 +854,7 @@ def parse(sre_obj, parent=None, regexp=None):
elif token[0] == sre_parse.ASSERT_NOT:
pass # TODO(buglloc): Do it!
else:
LOG.info('Unexpected token "{}"'.format(token[0]))
LOG.info('Unexpected token "{0}"'.format(token[0]))
return result

View File

@ -60,7 +60,7 @@ class Block(Directive):
self.children.append(directive)
def __str__(self):
return '{} {} {}'.format(self.name, ' '.join(self.args), '{')
return '{name} {args} {{'.format(name=self.name, args=' '.join(self.args))
class Root(Block):
@ -89,7 +89,7 @@ class ServerBlock(Block):
def __str__(self):
server_names = [str(sn) for sn in self.find('server_name')]
if server_names:
return 'server {{\n{}'.format('\n'.join(server_names[:2]))
return 'server {{\n{0}'.format('\n'.join(server_names[:2]))
return 'server {'
@ -141,10 +141,10 @@ class IfBlock(Block):
# if ($request_method = POST)
self.variable, self.operand, self.value = args
else:
raise Exception('Unknown "if" definition, args: {}'.format(repr(args)))
raise Exception('Unknown "if" definition, args: {0!r}'.format(args))
def __str__(self):
return '{} ({}) {{'.format(self.name, ' '.join(self.args))
return '{name} ({args}) {{'.format(name=self.name, args=' '.join(self.args))
class IncludeBlock(Block):
@ -156,7 +156,7 @@ class IncludeBlock(Block):
self.file_path = args[0]
def __str__(self):
return 'include {};'.format(self.file_path)
return 'include {0};'.format(self.file_path)
class MapBlock(Block):

View File

@ -41,7 +41,7 @@ class Directive(object):
raise NotImplementedError()
def __str__(self):
return '{} {};'.format(self.name, ' '.join(self.args))
return '{name} {args};'.format(name=self.name, args=' '.join(self.args))
class AddHeaderDirective(Directive):

View File

@ -4,7 +4,7 @@ from gixy.directives import block
class BaseFormatter(object):
skip_parents = {block.Root, block.HttpBlock}
skip_parents = set([block.Root, block.HttpBlock])
def format_reports(self, reports, stats):
raise NotImplementedError("Formatter must override format_reports function")
@ -74,11 +74,11 @@ class BaseFormatter(object):
if leap.is_block:
result.append('')
directive = str(leap).replace('\n', '\n' + '\t' * (level + 1))
result.append('{:s}{:s}'.format('\t' * level, directive))
result.append('{indent:s}{dir:s}'.format(indent='\t' * level, dir=directive))
if leap.is_block:
result.extend(self._traverse_tree(leap, points, level + 1 if printable else level))
if printable and have_parentheses:
result.append('{:s}}}'.format('\t' * level))
result.append('{indent:s}}}'.format(indent='\t' * level))
return result

View File

@ -22,7 +22,7 @@ class NginxParser(object):
self._init_directives()
def parse_file(self, path, root=None):
LOG.debug("Parse file: {}".format(path))
LOG.debug("Parse file: {0}".format(path))
content = open(path).read()
return self.parse(content=content, root=root, path_info=path)
@ -103,7 +103,7 @@ class NginxParser(object):
if self.is_dump:
return self._resolve_dump_include(pattern=pattern, parent=parent)
if not self.allow_includes:
LOG.debug('Includes are disallowed, skip: {}'.format(pattern))
LOG.debug('Includes are disallowed, skip: {0}'.format(pattern))
return
return self._resolve_file_include(pattern=pattern, parent=parent)
@ -117,7 +117,7 @@ class NginxParser(object):
self.parse_file(file_path, include)
if not file_path:
LOG.warning("File not found: {}".format(path))
LOG.warning("File not found: {0}".format(path))
def _resolve_dump_include(self, pattern, parent):
path = os.path.join(self.cwd, pattern)
@ -130,7 +130,7 @@ class NginxParser(object):
self.parse_block(parsed, include)
if not founded:
LOG.warning("File not found: {}".format(path))
LOG.warning("File not found: {0}".format(path))
def _prepare_dump(self, parsed_block):
filename = ''

View File

@ -33,7 +33,7 @@ def get_header_values(directive):
result = []
skip_next = False
for arg in directive.args:
if arg in {'-s', '-t'}:
if arg in ['-s', '-t']:
# Skip next value, because it's not a header
skip_next = True
elif arg.startswith('-'):

View File

@ -18,11 +18,11 @@ class add_header_redefinition(Plugin):
'See documentation: http://nginx.org/en/docs/http/ngx_http_headers_module.html#add_header')
help_url = 'https://github.com/yandex/gixy/blob/master/docs/en/plugins/addheaderredefinition.md'
directives = ['server', 'location', 'if']
options = {'headers': {'x-frame-options',
'x-content-type-options',
'x-xss-protection',
'content-security-policy',
'cache-control'}
options = {'headers': set(['x-frame-options',
'x-content-type-options',
'x-xss-protection',
'content-security-policy',
'cache-control'])
}
def __init__(self, config):

View File

@ -39,16 +39,16 @@ class origins(Plugin):
self.valid_re = re.compile(regex)
def audit(self, directive):
if directive.operand not in {'~', '~*', '!~', '!~*'}:
if directive.operand not in ['~', '~*', '!~', '!~*']:
# Not regexp
return
if directive.variable not in {'$http_referer', '$http_origin'}:
if directive.variable not in ['$http_referer', '$http_origin']:
# Not interesting
return
invalid_referers = set()
regexp = Regexp(directive.value, case_sensitive=(directive.operand in {'~', '!~'}))
regexp = Regexp(directive.value, case_sensitive=(directive.operand in ['~', '!~']))
for value in regexp.generate('/', anchored=True):
if value.startswith('^'):
value = value[1:]

36
tests/asserts.py Normal file
View File

@ -0,0 +1,36 @@
from nose.tools import assert_true, assert_false
'''
Various nose.tools helpers that doesn't exists in Python 2.6 Unittest :(
Must be removed with drop Python 2.6 support
'''
def assert_is_instance(obj, cls, msg=None):
"""Same as assert_true(isinstance(obj, cls)), with a nicer
default message."""
if not msg:
msg = '{orig} is not an instance of {test}'.format(orig=type(obj), test=cls)
assert_true(isinstance(obj, cls), msg=msg)
def assert_is_none(obj, msg=None):
"""Same as assert_true(obj is None), with a nicer default message."""
if not msg:
msg = '{orig!r} is not None'.format(orig=obj)
assert_true(obj is None, msg=msg)
def assert_is_not_none(obj, msg=None):
"""Same as assert_false(obj is None), with a nicer default message."""
if not msg:
msg = '{orig!r} is None'.format(orig=obj)
assert_false(obj is None, msg=msg)
def assert_in(member, container, msg=None):
"""Just like assert_true(a in b), but with a nicer default message."""
if not msg:
msg = '{member!r} not found in {container!r}'.format(member=member, container=container)
assert_true(member in container, msg=msg)

View File

@ -232,24 +232,24 @@ def test_negative_must_startswith():
def test_generate():
cases = (
(r'foo', {'foo'}),
(r'^sss', {'^sss'}),
(r'(1)(2)(3)', {'123'}),
(r'(1)((2)|(?:3))', {'12', '13'}),
(r'(^1?2?|aa/)', {'^', '^1', '^2', '^12', 'aa/'}),
(r'^https?://yandex.ru', {'^http://yandex|ru', '^https://yandex|ru'}),
(r'(^bb|11)$', {'^bb$', '11$'}),
(r'(http|https)', {'http', 'https'}),
(r'1*', {'', '11111'}),
(r'1*?', {'', '11111'}),
(r'1{0}?2', {'2'}),
(r'1{0}2', {'2'}),
(r'1+', {'11111'}),
(r'[^/]?', {'', '|'}),
(r'^http://(foo|bar)|baz', {'^http://foo', '^http://bar', 'baz'}),
(r'[^\x00-\x7b|\x7e-\xff]', {'\x7d'}),
(r'(a|b|c)', {'a', 'b', 'c'}),
(r'[xyz]', {'x', 'y', 'z'})
(r'foo', ['foo']),
(r'^sss', ['^sss']),
(r'(1)(2)(3)', ['123']),
(r'(1)((2)|(?:3))', ['12', '13']),
(r'(^1?2?|aa/)', ['^', '^1', '^2', '^12', 'aa/']),
(r'^https?://yandex.ru', ['^http://yandex|ru', '^https://yandex|ru']),
(r'(^bb|11)$', ['^bb$', '11$']),
(r'(http|https)', ['http', 'https']),
(r'1*', ['', '11111']),
(r'1*?', ['', '11111']),
(r'1[0]?2', ['102', '12']),
(r'1[0]2', ['102']),
(r'1+', ['11111']),
(r'[^/]?', ['', '|']),
(r'^http://(foo|bar)|baz', ['^http://foo', '^http://bar', 'baz']),
(r'[^\x00-\x7b|\x7e-\xff]', ['\x7d']),
(r'(a|b|c)', ['a', 'b', 'c']),
(r'[xyz]', ['x', 'y', 'z'])
)
for case in cases:
regexp, values = case
@ -258,7 +258,7 @@ def test_generate():
def test_strict_generate():
reg = Regexp('^foo|bar', strict=True)
assert_equals(sorted(reg.generate('|', anchored=True)), sorted({'^foo', '^bar'}))
assert_equals(sorted(reg.generate('|', anchored=True)), sorted(['^foo', '^bar']))
def test_gen_anchor():
@ -284,63 +284,63 @@ def test_group_can_contains():
source = '/some/(?P<action>[^/:.]+)/'
reg = Regexp(source)
assert_true(reg.can_contain('\n'),
'Whole regex "{}" can contains "{}"'.format(source, '\\n'))
'Whole regex "{src}" can contains {sym!r}'.format(src=source, sym='\\n'))
assert_true(reg.group(0).can_contain('\n'),
'Group 0 from regex "{}" can contains "{}"'.format(source, '\\n'))
'Group 0 from regex "{src}" can contains {sym!r}'.format(src=source, sym='\\n'))
assert_true(reg.group('action').can_contain('\n'),
'Group "action" from regex "{}" can contains "{}"'.format(source, '\\n'))
'Group "action" from regex "{src}" can contains {sym!r}'.format(src=source, sym='\\n'))
assert_true(reg.group(1).can_contain('\n'),
'Group 1 from regex "{}" can contains "{}"'.format(source, '\\n'))
'Group 1 from regex "{src}" can contains {sym!r}'.format(src=source, sym='\\n'))
assert_false(reg.group('action').can_contain('/'),
'Group "action" from regex "{}" CAN\'T (!) contain "{}"'.format(source, '/'))
'Group "action" from regex "{src}" CAN\'T (!) contain {sym!r}'.format(src=source, sym='/'))
def check_positive_contain(regexp, char):
reg = Regexp(regexp, case_sensitive=True)
assert_true(reg.can_contain(char),
'"{}" should contain "{}"'.format(regexp, char))
'{reg!r} should contain {chr!r}'.format(reg=regexp, chr=char))
reg = Regexp(regexp, case_sensitive=False)
char = char.upper()
assert_true(reg.can_contain(char),
'"{}" (case insensitive) should contain "{}"'.format(regexp, char))
'{reg!r} (case insensitive) should contain {chr!r}'.format(reg=regexp, chr=char))
def check_negative_contain(regexp, char):
reg = Regexp(regexp, case_sensitive=True)
assert_false(reg.can_contain(char),
'"{}" should not contain "{}"'.format(regexp, char))
'{reg!r} should not contain {chr!r}'.format(reg=regexp, chr=char))
reg = Regexp(regexp, case_sensitive=False)
char = char.upper()
assert_false(reg.can_contain(char),
'"{}" (case insensitive) should not contain "{}"'.format(regexp, char))
'{reg!r} (case insensitive) should not contain {chr!r}'.format(reg=regexp, chr=char))
def check_positive_startswith(regexp, char, strict):
reg = Regexp(regexp, case_sensitive=True, strict=strict)
assert_true(reg.can_startswith(char),
'"{}" can start\'s with "{}"'.format(regexp, char))
'{reg!r} can start\'s with {chr!r}'.format(reg=regexp, chr=char))
reg = Regexp(regexp, case_sensitive=False, strict=strict)
char = char.upper()
assert_true(reg.can_startswith(char),
'"{}" (case insensitive) can start\'s with "{}"'.format(regexp, char))
'{reg!r} (case insensitive) can start\'s with {chr!r}'.format(reg=regexp, chr=char))
def check_negative_startswith(regexp, char, strict):
reg = Regexp(regexp, case_sensitive=True, strict=strict)
assert_false(reg.can_startswith(char),
'"{}" can\'t start\'s with "{}"'.format(regexp, char))
'{reg!r} can\'t start\'s with {chr!r}'.format(reg=regexp, chr=char))
reg = Regexp(regexp, case_sensitive=False, strict=strict)
char = char.upper()
assert_false(reg.can_startswith(char),
'"{}" (case insensitive) can\'t start\'s with "{}"'.format(regexp, char))
'{reg!r} (case insensitive) can\'t start\'s with {chr!r}'.format(reg=regexp, chr=char))
def check_groups_names(regexp, groups):
@ -356,45 +356,46 @@ def check_to_string(regexp, string):
def check_positive_must_contain(regexp, char):
reg = Regexp(regexp, case_sensitive=True)
assert_true(reg.must_contain(char),
'"{}" must contain with "{}"'.format(regexp, char))
'{reg!r} must contain with {chr!r}'.format(reg=regexp, chr=char))
reg = Regexp(regexp, case_sensitive=False)
char = char.upper()
assert_true(reg.must_contain(char),
'"{}" (case insensitive) must contain with "{}"'.format(regexp, char))
'{reg!r} (case insensitive) must contain with {chr!r}'.format(reg=regexp, chr=char))
def check_negative_must_contain(regexp, char):
reg = Regexp(regexp, case_sensitive=True)
assert_false(reg.must_contain(char),
'"{}" must NOT contain with "{}"'.format(regexp, char))
'{reg!r} must NOT contain with {chr!r}'.format(reg=regexp, chr=char))
reg = Regexp(regexp, case_sensitive=False)
char = char.upper()
assert_false(reg.must_contain(char),
'"{}" (case insensitive) must NOT contain with "{}"'.format(regexp, char))
'{reg!r} (case insensitive) must NOT contain with {chr!r}'.format(reg=regexp, chr=char))
def check_positive_must_startswith(regexp, char, strict):
reg = Regexp(regexp, case_sensitive=True, strict=strict)
assert_true(reg.must_startswith(char),
'"{}" MUST start\'s with "{}"'.format(regexp, char))
'{reg!r} MUST start\'s with {chr!r}'.format(reg=regexp, chr=char))
reg = Regexp(regexp, case_sensitive=False, strict=strict)
char = char.upper()
assert_true(reg.must_startswith(char),
'"{}" (case insensitive) MUST start\'s with "{}"'.format(regexp, char))
'{reg!r} (case insensitive) MUST start\'s with {chr!r}'.format(reg=regexp, chr=char))
def check_negative_must_startswith(regexp, char, strict):
reg = Regexp(regexp, case_sensitive=True, strict=strict)
assert_false(reg.must_startswith(char),
'"{}" MUST NOT start\'s with "{}"'.format(regexp, char))
'{reg!r} MUST NOT start\'s with {chr!r}'.format(reg=regexp, chr=char))
reg = Regexp(regexp, case_sensitive=False, strict=strict)
char = char.upper()
assert_false(reg.must_startswith(char),
'"{}" (case insensitive) MUST NOT start\'s with "{}"'.format(regexp, char))
'{reg!r} (case insensitive) MUST NOT start\'s with {chr!r}'.format(reg=regexp, chr=char))
def check_generate(regexp, values):
reg = Regexp(regexp)

View File

@ -1,4 +1,5 @@
from nose.tools import assert_equals, assert_is_instance, assert_is_not_none, assert_is_none, assert_true, assert_false
from nose.tools import assert_equals, assert_true, assert_false
from tests.asserts import assert_is_instance, assert_is_none, assert_is_not_none
from gixy.parser.nginx_parser import NginxParser
from gixy.directives.block import *
@ -148,7 +149,7 @@ def test_block_some_flat():
'''
directive = _get_parsed(config)
for d in {'default_type', 'sendfile', 'keepalive_timeout'}:
for d in ['default_type', 'sendfile', 'keepalive_timeout']:
c = directive.some(d, flat=True)
assert_is_not_none(c)
assert_equals(c.name, d)

View File

@ -1,4 +1,5 @@
from nose.tools import assert_equals, assert_is_instance, assert_false, assert_true
from nose.tools import assert_equals, assert_false, assert_true
from tests.asserts import assert_is_instance
from gixy.parser.nginx_parser import NginxParser
from gixy.directives.directive import *

View File

@ -1,4 +1,5 @@
from nose.tools import assert_is_instance, assert_equal
from nose.tools import assert_equal
from tests.asserts import assert_is_instance
from gixy.parser.nginx_parser import NginxParser
from gixy.directives.directive import *
from gixy.directives.block import *

View File

@ -1,4 +1,5 @@
from nose.tools import assert_equals, assert_true, assert_in
from nose.tools import assert_equals, assert_true
from tests.asserts import assert_in
import os
from os import path
import json
@ -55,9 +56,9 @@ def test_from_config():
for plugin in manager.plugins:
plugin = plugin.name
assert_true(plugin in tested_plugins,
'Plugin "{}" should have at least one simple test config'.format(plugin))
'Plugin {name!r} should have at least one simple test config'.format(name=plugin))
assert_true(plugin in tested_fp_plugins,
'Plugin "{}" should have at least one simple test config with false positive'.format(plugin))
'Plugin {name!r} should have at least one simple test config with false positive'.format(name=plugin))
def parse_plugin_options(config_path):

View File

@ -1,5 +1,5 @@
[tox]
envlist = py27, py34, py35, py36, flake8
envlist = py26, py27, py34, py35, py36, flake8
skip_missing_interpreters = True
[testenv]
@ -16,4 +16,4 @@ commands =
flake8 setup.py gixy
[flake8]
max_line_length = 120
max_line_length = 120