mirror of
https://github.com/PyCQA/flake8.git
synced 2026-03-29 10:36:53 +00:00
359 lines
12 KiB
Python
359 lines
12 KiB
Python
"""Tests for the FileProcessor class."""
|
|
import ast
|
|
import optparse
|
|
import tokenize
|
|
|
|
import mock
|
|
import pytest
|
|
|
|
from flake8 import processor
|
|
|
|
|
|
def options_from(**kwargs):
|
|
"""Generate a Values instances with our kwargs."""
|
|
kwargs.setdefault('hang_closing', True)
|
|
kwargs.setdefault('max_line_length', 79)
|
|
kwargs.setdefault('verbose', False)
|
|
kwargs.setdefault('stdin_display_name', 'stdin')
|
|
return optparse.Values(kwargs)
|
|
|
|
|
|
def test_read_lines_splits_lines():
|
|
"""Verify that read_lines splits the lines of the file."""
|
|
file_processor = processor.FileProcessor(__file__, options_from())
|
|
lines = file_processor.lines
|
|
assert len(lines) > 5
|
|
assert any('"""Tests for the FileProcessor class."""' in line.rstrip()
|
|
for line in lines)
|
|
|
|
|
|
def _lines_from_file(tmpdir, contents):
|
|
f = tmpdir.join('f.py')
|
|
# be careful to write the bytes exactly to avoid newline munging
|
|
f.write_binary(contents)
|
|
return processor.FileProcessor(f.strpath, options_from()).lines
|
|
|
|
|
|
def test_read_lines_universal_newlines(tmpdir):
|
|
r"""Verify that line endings are translated to \n."""
|
|
lines = _lines_from_file(tmpdir, b'# coding: utf-8\r\nx = 1\r\n')
|
|
assert lines == ['# coding: utf-8\n', 'x = 1\n']
|
|
|
|
|
|
def test_read_lines_incorrect_utf_16(tmpdir):
|
|
"""Verify that an incorrectly encoded file is read as latin-1."""
|
|
lines = _lines_from_file(tmpdir, b'# coding: utf16\nx = 1\n')
|
|
assert lines == ['# coding: utf16\n', 'x = 1\n']
|
|
|
|
|
|
def test_read_lines_unknown_encoding(tmpdir):
|
|
"""Verify that an unknown encoding is still read as latin-1."""
|
|
lines = _lines_from_file(tmpdir, b'# coding: fake-encoding\nx = 1\n')
|
|
assert lines == ['# coding: fake-encoding\n', 'x = 1\n']
|
|
|
|
|
|
@pytest.mark.parametrize('first_line', [
|
|
'\xEF\xBB\xBF"""Module docstring."""\n',
|
|
u'\uFEFF"""Module docstring."""\n',
|
|
])
|
|
def test_strip_utf_bom(first_line):
|
|
r"""Verify that we strip '\xEF\xBB\xBF' from the first line."""
|
|
lines = [first_line]
|
|
file_processor = processor.FileProcessor('-', options_from(), lines[:])
|
|
assert file_processor.lines != lines
|
|
assert file_processor.lines[0] == '"""Module docstring."""\n'
|
|
|
|
|
|
@pytest.mark.parametrize('lines, expected', [
|
|
(['\xEF\xBB\xBF"""Module docstring."""\n'], False),
|
|
([u'\uFEFF"""Module docstring."""\n'], False),
|
|
(['#!/usr/bin/python', '# flake8 is great', 'a = 1'], False),
|
|
(['#!/usr/bin/python', '# flake8: noqa', 'a = 1'], True),
|
|
(['#!/usr/bin/python', '# flake8:noqa', 'a = 1'], True),
|
|
(['# flake8: noqa', '#!/usr/bin/python', 'a = 1'], True),
|
|
(['# flake8:noqa', '#!/usr/bin/python', 'a = 1'], True),
|
|
(['#!/usr/bin/python', 'a = 1', '# flake8: noqa'], True),
|
|
(['#!/usr/bin/python', 'a = 1', '# flake8:noqa'], True),
|
|
(['#!/usr/bin/python', 'a = 1 # flake8: noqa'], False),
|
|
(['#!/usr/bin/python', 'a = 1 # flake8:noqa'], False),
|
|
])
|
|
def test_should_ignore_file(lines, expected):
|
|
"""Verify that we ignore a file if told to."""
|
|
file_processor = processor.FileProcessor('-', options_from(), lines)
|
|
assert file_processor.should_ignore_file() is expected
|
|
|
|
|
|
@mock.patch('flake8.utils.stdin_get_value')
|
|
def test_read_lines_from_stdin(stdin_get_value):
|
|
"""Verify that we use our own utility function to retrieve stdin."""
|
|
stdin_value = mock.Mock()
|
|
stdin_value.splitlines.return_value = []
|
|
stdin_get_value.return_value = stdin_value
|
|
processor.FileProcessor('-', options_from())
|
|
stdin_get_value.assert_called_once_with()
|
|
stdin_value.splitlines.assert_called_once_with(True)
|
|
|
|
|
|
@mock.patch('flake8.utils.stdin_get_value')
|
|
def test_stdin_filename_attribute(stdin_get_value):
|
|
"""Verify that we update the filename attribute."""
|
|
stdin_value = mock.Mock()
|
|
stdin_value.splitlines.return_value = []
|
|
stdin_get_value.return_value = stdin_value
|
|
file_processor = processor.FileProcessor('-', options_from())
|
|
assert file_processor.filename == 'stdin'
|
|
|
|
|
|
@mock.patch('flake8.utils.stdin_get_value')
|
|
def test_read_lines_uses_display_name(stdin_get_value):
|
|
"""Verify that when processing stdin we use a display name if present."""
|
|
stdin_value = mock.Mock()
|
|
stdin_value.splitlines.return_value = []
|
|
stdin_get_value.return_value = stdin_value
|
|
file_processor = processor.FileProcessor('-', options_from(
|
|
stdin_display_name='display_name.py'
|
|
))
|
|
assert file_processor.filename == 'display_name.py'
|
|
|
|
|
|
@mock.patch('flake8.utils.stdin_get_value')
|
|
def test_read_lines_ignores_empty_display_name(stdin_get_value):
|
|
"""Verify that when processing stdin we use a display name if present."""
|
|
stdin_value = mock.Mock()
|
|
stdin_value.splitlines.return_value = []
|
|
stdin_get_value.return_value = stdin_value
|
|
file_processor = processor.FileProcessor('-', options_from(
|
|
stdin_display_name=''
|
|
))
|
|
assert file_processor.filename == 'stdin'
|
|
|
|
|
|
def test_line_for():
|
|
"""Verify we grab the correct line from the cached lines."""
|
|
file_processor = processor.FileProcessor('-', options_from(), lines=[
|
|
'Line 1',
|
|
'Line 2',
|
|
'Line 3',
|
|
])
|
|
|
|
for i in range(1, 4):
|
|
assert file_processor.line_for(i) == 'Line {0}'.format(i)
|
|
|
|
|
|
def test_next_line():
|
|
"""Verify we update the file_processor state for each new line."""
|
|
file_processor = processor.FileProcessor('-', options_from(), lines=[
|
|
'Line 1',
|
|
'Line 2',
|
|
'Line 3',
|
|
])
|
|
|
|
for i in range(1, 4):
|
|
assert file_processor.next_line() == 'Line {}'.format(i)
|
|
assert file_processor.line_number == i
|
|
|
|
|
|
@pytest.mark.parametrize('error_code, line, expected_indent_char', [
|
|
('E101', '\t\ta = 1', '\t'),
|
|
('E101', ' a = 1', ' '),
|
|
('W101', 'frobulate()', None),
|
|
('F821', 'class FizBuz:', None),
|
|
])
|
|
def test_check_physical_error(error_code, line, expected_indent_char):
|
|
"""Verify we update the indet char for the appropriate error code."""
|
|
file_processor = processor.FileProcessor('-', options_from(), lines=[
|
|
'Line 1',
|
|
])
|
|
|
|
file_processor.check_physical_error(error_code, line)
|
|
assert file_processor.indent_char == expected_indent_char
|
|
|
|
|
|
@pytest.mark.parametrize('params, args, expected_kwargs', [
|
|
({'blank_before': True, 'blank_lines': True},
|
|
None,
|
|
{'blank_before': 0, 'blank_lines': 0}),
|
|
({'noqa': True, 'fake': True},
|
|
{'fake': 'foo'},
|
|
{'noqa': False, 'fake': 'foo'}),
|
|
({'blank_before': True, 'blank_lines': True, 'noqa': True},
|
|
{'blank_before': 10, 'blank_lines': 5, 'noqa': True},
|
|
{'blank_before': 10, 'blank_lines': 5, 'noqa': True}),
|
|
({}, {'fake': 'foo'}, {'fake': 'foo'}),
|
|
({'non-existent': False}, {'fake': 'foo'}, {'fake': 'foo'}),
|
|
])
|
|
def test_keyword_arguments_for(params, args, expected_kwargs):
|
|
"""Verify the keyword args are generated properly."""
|
|
file_processor = processor.FileProcessor('-', options_from(), lines=[
|
|
'Line 1',
|
|
])
|
|
kwargs_for = file_processor.keyword_arguments_for
|
|
|
|
assert kwargs_for(params, args) == expected_kwargs
|
|
|
|
|
|
def test_keyword_arguments_for_does_not_handle_attribute_errors():
|
|
"""Verify we re-raise AttributeErrors."""
|
|
file_processor = processor.FileProcessor('-', options_from(), lines=[
|
|
'Line 1',
|
|
])
|
|
|
|
with pytest.raises(AttributeError):
|
|
file_processor.keyword_arguments_for(['fake'])
|
|
|
|
|
|
@pytest.mark.parametrize('unsplit_line, expected_lines', [
|
|
('line', []),
|
|
('line 1\n', ['line 1']),
|
|
('line 1\nline 2\n', ['line 1', 'line 2']),
|
|
('line 1\n\nline 2\n', ['line 1', '', 'line 2']),
|
|
])
|
|
def test_split_line(unsplit_line, expected_lines):
|
|
"""Verify the token line spliting."""
|
|
file_processor = processor.FileProcessor('-', options_from(), lines=[
|
|
'Line 1',
|
|
])
|
|
|
|
actual_lines = list(file_processor.split_line((1, unsplit_line)))
|
|
assert expected_lines == actual_lines
|
|
|
|
assert len(actual_lines) == file_processor.line_number
|
|
|
|
|
|
def test_build_ast():
|
|
"""Verify the logic for how we build an AST for plugins."""
|
|
file_processor = processor.FileProcessor('-', options_from(), lines=[
|
|
'a = 1\n'
|
|
])
|
|
|
|
module = file_processor.build_ast()
|
|
assert isinstance(module, ast.Module)
|
|
|
|
|
|
def test_next_logical_line_updates_the_previous_logical_line():
|
|
"""Verify that we update our tracking of the previous logical line."""
|
|
file_processor = processor.FileProcessor('-', options_from(), lines=[
|
|
'a = 1\n'
|
|
])
|
|
|
|
file_processor.indent_level = 1
|
|
file_processor.logical_line = 'a = 1'
|
|
assert file_processor.previous_logical == ''
|
|
assert file_processor.previous_indent_level == 0
|
|
|
|
file_processor.next_logical_line()
|
|
assert file_processor.previous_logical == 'a = 1'
|
|
assert file_processor.previous_indent_level == 1
|
|
|
|
|
|
def test_visited_new_blank_line():
|
|
"""Verify we update the number of blank lines seen."""
|
|
file_processor = processor.FileProcessor('-', options_from(), lines=[
|
|
'a = 1\n'
|
|
])
|
|
|
|
assert file_processor.blank_lines == 0
|
|
file_processor.visited_new_blank_line()
|
|
assert file_processor.blank_lines == 1
|
|
|
|
|
|
def test_inside_multiline():
|
|
"""Verify we update the line number and reset multiline."""
|
|
file_processor = processor.FileProcessor('-', options_from(), lines=[
|
|
'a = 1\n'
|
|
])
|
|
|
|
assert file_processor.multiline is False
|
|
assert file_processor.line_number == 0
|
|
with file_processor.inside_multiline(10):
|
|
assert file_processor.multiline is True
|
|
assert file_processor.line_number == 10
|
|
|
|
assert file_processor.multiline is False
|
|
|
|
|
|
@pytest.mark.parametrize('string, expected', [
|
|
('""', '""'),
|
|
("''", "''"),
|
|
('"a"', '"x"'),
|
|
("'a'", "'x'"),
|
|
('"x"', '"x"'),
|
|
("'x'", "'x'"),
|
|
('"abcdef"', '"xxxxxx"'),
|
|
("'abcdef'", "'xxxxxx'"),
|
|
('""""""', '""""""'),
|
|
("''''''", "''''''"),
|
|
('"""a"""', '"""x"""'),
|
|
("'''a'''", "'''x'''"),
|
|
('"""x"""', '"""x"""'),
|
|
("'''x'''", "'''x'''"),
|
|
('"""abcdef"""', '"""xxxxxx"""'),
|
|
("'''abcdef'''", "'''xxxxxx'''"),
|
|
('"""xxxxxx"""', '"""xxxxxx"""'),
|
|
("'''xxxxxx'''", "'''xxxxxx'''"),
|
|
])
|
|
def test_mutate_string(string, expected):
|
|
"""Verify we appropriately mutate the string to sanitize it."""
|
|
actual = processor.mutate_string(string)
|
|
assert expected == actual
|
|
|
|
|
|
@pytest.mark.parametrize('string, expected', [
|
|
(' ', 4),
|
|
(' ', 6),
|
|
('\t', 8),
|
|
('\t\t', 16),
|
|
(' \t', 8),
|
|
(' \t', 16),
|
|
])
|
|
def test_expand_indent(string, expected):
|
|
"""Verify we correctly measure the amount of indentation."""
|
|
actual = processor.expand_indent(string)
|
|
assert expected == actual
|
|
|
|
|
|
@pytest.mark.parametrize('token, log_string', [
|
|
[(tokenize.COMMENT, '# this is a comment',
|
|
(1, 0), # (start_row, start_column)
|
|
(1, 19), # (end_ro, end_column)
|
|
'# this is a comment',),
|
|
"l.1\t[:19]\tCOMMENT\t'# this is a comment'"],
|
|
[(tokenize.COMMENT, '# this is a comment',
|
|
(1, 5), # (start_row, start_column)
|
|
(1, 19), # (end_ro, end_column)
|
|
'# this is a comment',),
|
|
"l.1\t[5:19]\tCOMMENT\t'# this is a comment'"],
|
|
[(tokenize.COMMENT, '# this is a comment',
|
|
(1, 0), # (start_row, start_column)
|
|
(2, 19), # (end_ro, end_column)
|
|
'# this is a comment',),
|
|
"l.1\tl.2\tCOMMENT\t'# this is a comment'"],
|
|
])
|
|
def test_log_token(token, log_string):
|
|
"""Verify we use the log object passed in."""
|
|
log = mock.Mock()
|
|
processor.log_token(log, token)
|
|
log.log.assert_called_once_with(
|
|
5, # flake8._EXTRA_VERBOSE
|
|
log_string,
|
|
)
|
|
|
|
|
|
@pytest.mark.parametrize('current_count, token_text, expected', [
|
|
(None, '(', 1),
|
|
(None, '[', 1),
|
|
(None, '{', 1),
|
|
(1, ')', 0),
|
|
(1, ']', 0),
|
|
(1, '}', 0),
|
|
(10, '+', 10),
|
|
])
|
|
def test_count_parentheses(current_count, token_text, expected):
|
|
"""Verify our arithmetic is correct."""
|
|
assert processor.count_parentheses(current_count, token_text) == expected
|
|
|
|
|
|
def test_nonexistent_file():
|
|
"""Verify that FileProcessor raises IOError when a file does not exist."""
|
|
with pytest.raises(IOError):
|
|
processor.FileProcessor("foobar.py", options_from())
|