"""Integration tests for the main entrypoint of flake8.""" import json import os import mock import pytest from flake8 import utils from flake8.main import cli def _call_main(argv, retv=0): with pytest.raises(SystemExit) as excinfo: cli.main(argv) assert excinfo.value.code == retv def test_diff_option(tmpdir, capsys): """Ensure that `flake8 --diff` works.""" t_py_contents = '''\ import os import sys # unused but not part of diff print('(to avoid trailing whitespace in test)') print('(to avoid trailing whitespace in test)') print(os.path.join('foo', 'bar')) y # part of the diff and an error ''' diff = '''\ diff --git a/t.py b/t.py index d64ac39..7d943de 100644 --- a/t.py +++ b/t.py @@ -4,3 +4,5 @@ import sys # unused but not part of diff print('(to avoid trailing whitespace in test)') print('(to avoid trailing whitespace in test)') print(os.path.join('foo', 'bar')) + +y # part of the diff and an error ''' with mock.patch.object(utils, 'stdin_get_value', return_value=diff): with tmpdir.as_cwd(): tmpdir.join('t.py').write(t_py_contents) _call_main(['--diff'], retv=1) out, err = capsys.readouterr() assert out == "t.py:8:1: F821 undefined name 'y'\n" assert err == '' def test_form_feed_line_split(tmpdir, capsys): """Test that form feed is treated the same for stdin.""" src = 'x=1\n\f\ny=1\n' expected_out = '''\ t.py:1:2: E225 missing whitespace around operator t.py:3:2: E225 missing whitespace around operator ''' with tmpdir.as_cwd(): tmpdir.join('t.py').write(src) with mock.patch.object(utils, 'stdin_get_value', return_value=src): _call_main(['-', '--stdin-display-name=t.py'], retv=1) out, err = capsys.readouterr() assert out == expected_out assert err == '' _call_main(['t.py'], retv=1) out, err = capsys.readouterr() assert out == expected_out assert err == '' def test_e101_indent_char_does_not_reset(tmpdir, capsys): """Ensure that E101 with an existing indent_char does not reset it.""" t_py_contents = """\ if True: print('space indented') s = '''\ \ttab indented ''' # noqa: E101 if True: print('space indented') """ with tmpdir.as_cwd(): tmpdir.join('t.py').write(t_py_contents) _call_main(['t.py']) def test_statistics_option(tmpdir, capsys): """Ensure that `flake8 --statistics` works.""" with tmpdir.as_cwd(): tmpdir.join('t.py').write('import os\nimport sys\n') _call_main(['--statistics', 't.py'], retv=1) out, err = capsys.readouterr() assert out == '''\ t.py:1:1: F401 'os' imported but unused t.py:2:1: F401 'sys' imported but unused 2 F401 'os' imported but unused ''' assert err == '' def test_show_source_option(tmpdir, capsys): """Ensure that --show-source and --no-show-source work.""" with tmpdir.as_cwd(): tmpdir.join('tox.ini').write('[flake8]\nshow_source = true\n') tmpdir.join('t.py').write('import os\n') _call_main(['t.py'], retv=1) out, err = capsys.readouterr() assert out == '''\ t.py:1:1: F401 'os' imported but unused import os ^ ''' assert err == '' with tmpdir.as_cwd(): _call_main(['t.py', '--no-show-source'], retv=1) out, err = capsys.readouterr() assert out == '''\ t.py:1:1: F401 'os' imported but unused ''' assert err == '' def test_extend_exclude(tmpdir, capsys): """Ensure that `flake8 --extend-exclude` works.""" for d in ['project', 'vendor', 'legacy', '.git', '.tox', '.hg']: tmpdir.mkdir(d).join('t.py').write('import os\nimport sys\n') with tmpdir.as_cwd(): _call_main(['--extend-exclude=vendor,legacy/'], retv=1) out, err = capsys.readouterr() expected_out = '''\ ./project/t.py:1:1: F401 'os' imported but unused ./project/t.py:2:1: F401 'sys' imported but unused ''' assert out == expected_out.replace('/', os.sep) assert err == '' def test_malformed_per_file_ignores_error(tmpdir, capsys): """Test the error message for malformed `per-file-ignores`.""" setup_cfg = '''\ [flake8] per-file-ignores = incorrect/* values/* ''' with tmpdir.as_cwd(): tmpdir.join('setup.cfg').write(setup_cfg) _call_main(['.'], retv=1) out, err = capsys.readouterr() assert out == '''\ There was a critical error during execution of Flake8: Expected `per-file-ignores` to be a mapping from file exclude patterns to ignore codes. Configured `per-file-ignores` setting: incorrect/* values/* ''' # noqa: E501 def test_tokenization_error_but_not_syntax_error(tmpdir, capsys): """Test that flake8 does not crash on tokenization errors.""" with tmpdir.as_cwd(): # this is a crash in the tokenizer, but not in the ast tmpdir.join('t.py').write("b'foo' \\\n") _call_main(['t.py'], retv=1) out, err = capsys.readouterr() assert out == 't.py:1:1: E902 TokenError: EOF in multi-line statement\n' assert err == '' def test_tokenization_error_is_a_syntax_error(tmpdir, capsys): """Test when tokenize raises a SyntaxError.""" with tmpdir.as_cwd(): tmpdir.join('t.py').write('if True:\n pass\n pass\n') _call_main(['t.py'], retv=1) out, err = capsys.readouterr() assert out == 't.py:1:1: E902 IndentationError: unindent does not match any outer indentation level\n' # noqa: E501 assert err == '' def test_bug_report_successful(capsys): """Test that --bug-report does not crash.""" _call_main(['--bug-report']) out, err = capsys.readouterr() assert json.loads(out) assert err == '' def test_specific_noqa_does_not_clobber_pycodestyle_noqa(tmpdir, capsys): """See https://gitlab.com/pycqa/flake8/issues/552.""" with tmpdir.as_cwd(): tmpdir.join('t.py').write("test = ('ABC' == None) # noqa: E501\n") _call_main(['t.py'], retv=1) out, err = capsys.readouterr() assert out == '''\ t.py:1:15: E711 comparison to None should be 'if cond is None:' ''' def test_specific_noqa_on_line_with_continuation(tmpdir, capsys): """See https://gitlab.com/pycqa/flake8/issues/375.""" t_py_src = '''\ from os \\ import path # noqa: F401 x = """ trailing whitespace: \n """ # noqa: W291 ''' with tmpdir.as_cwd(): tmpdir.join('t.py').write(t_py_src) _call_main(['t.py'], retv=0) out, err = capsys.readouterr() assert out == err == '' def test_physical_line_file_not_ending_in_newline(tmpdir, capsys): """See https://github.com/PyCQA/pycodestyle/issues/960.""" t_py_src = 'def f():\n\tpass' with tmpdir.as_cwd(): tmpdir.join('t.py').write(t_py_src) _call_main(['t.py'], retv=1) out, err = capsys.readouterr() assert out == '''\ t.py:2:1: W191 indentation contains tabs t.py:2:6: W292 no newline at end of file ''' def test_physical_line_file_not_ending_in_newline_trailing_ws(tmpdir, capsys): """See https://github.com/PyCQA/pycodestyle/issues/960.""" t_py_src = 'x = 1 ' with tmpdir.as_cwd(): tmpdir.join('t.py').write(t_py_src) _call_main(['t.py'], retv=1) out, err = capsys.readouterr() assert out == '''\ t.py:1:6: W291 trailing whitespace t.py:1:9: W292 no newline at end of file ''' def test_obtaining_args_from_sys_argv_when_not_explicity_provided(capsys): """Test that arguments are obtained from 'sys.argv'.""" with mock.patch('sys.argv', ['flake8', '--help']): _call_main(None) out, err = capsys.readouterr() assert out.startswith('usage: flake8 [options] file file ...\n') assert err == '' def test_cli_config_option_respected(tmp_path): """Test --config is used.""" config = tmp_path / "flake8.ini" config.write_text(u"""\ [flake8] ignore = F401 """) py_file = tmp_path / "t.py" py_file.write_text(u"import os\n") _call_main(["--config", str(config), str(py_file)]) def test_cli_isolated_overrides_config_option(tmp_path): """Test --isolated overrides --config.""" config = tmp_path / "flake8.ini" config.write_text(u"""\ [flake8] ignore = F401 """) py_file = tmp_path / "t.py" py_file.write_text(u"import os\n") _call_main(["--isolated", "--config", str(config), str(py_file)], retv=1) def test_file_not_found(tmpdir, capsys): """Ensure that a not-found file / directory is an error.""" with tmpdir.as_cwd(): _call_main(["i-do-not-exist"], retv=1) out, err = capsys.readouterr() assert out.startswith("i-do-not-exist:0:1: E902") assert err == "" def test_output_file(tmpdir, capsys): """Ensure that --output-file is honored.""" tmpdir.join('t.py').write('import os\n') with tmpdir.as_cwd(): _call_main(['t.py', '--output-file=f'], retv=1) out, err = capsys.readouterr() assert out == err == "" expected = "t.py:1:1: F401 'os' imported but unused\n" assert tmpdir.join('f').read() == expected