mirror of
https://github.com/PyCQA/flake8.git
synced 2026-04-08 05:54:17 +00:00
Lint Python files with a shebang
When a pattern is not passed to flake8 (--filename), look for all files that end with .py as well as extension-less files that start with a Python shebang. Helps project lint scripts that may not have an extension. Fixes #409
This commit is contained in:
parent
a2b7a7e4c5
commit
36a70fd110
4 changed files with 79 additions and 3 deletions
|
|
@ -3,6 +3,7 @@ import collections
|
||||||
import errno
|
import errno
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
import signal
|
import signal
|
||||||
import sys
|
import sys
|
||||||
import tokenize
|
import tokenize
|
||||||
|
|
@ -54,6 +55,8 @@ class Manager(object):
|
||||||
together and make our output deterministic.
|
together and make our output deterministic.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
SHEBANG_RE = re.compile(br'^#!.*\bpython[23w]?\b')
|
||||||
|
|
||||||
def __init__(self, style_guide, arguments, checker_plugins):
|
def __init__(self, style_guide, arguments, checker_plugins):
|
||||||
"""Initialize our Manager instance.
|
"""Initialize our Manager instance.
|
||||||
|
|
||||||
|
|
@ -199,9 +202,26 @@ class Manager(object):
|
||||||
paths = ['.']
|
paths = ['.']
|
||||||
|
|
||||||
filename_patterns = self.options.filename
|
filename_patterns = self.options.filename
|
||||||
|
patterns_exist = True
|
||||||
|
if not filename_patterns:
|
||||||
|
filename_patterns = ['*.py']
|
||||||
|
patterns_exist = False
|
||||||
running_from_vcs = self.options._running_from_vcs
|
running_from_vcs = self.options._running_from_vcs
|
||||||
running_from_diff = self.options.diff
|
running_from_diff = self.options.diff
|
||||||
|
|
||||||
|
def has_shebang(filename):
|
||||||
|
if patterns_exist:
|
||||||
|
# If a user explicitly specifies something, e.g. ``*.py``,
|
||||||
|
# don't inspect the shebang.
|
||||||
|
return False
|
||||||
|
|
||||||
|
if not os.path.isfile(filename):
|
||||||
|
return False
|
||||||
|
|
||||||
|
with open(filename, 'rb') as fp:
|
||||||
|
line = fp.readline(100)
|
||||||
|
return bool(self.SHEBANG_RE.match(line))
|
||||||
|
|
||||||
# NOTE(sigmavirus24): Yes this is a little unsightly, but it's our
|
# NOTE(sigmavirus24): Yes this is a little unsightly, but it's our
|
||||||
# best solution right now.
|
# best solution right now.
|
||||||
def should_create_file_checker(filename, argument):
|
def should_create_file_checker(filename, argument):
|
||||||
|
|
@ -220,8 +240,8 @@ class Manager(object):
|
||||||
explicitly_provided = (not running_from_vcs and
|
explicitly_provided = (not running_from_vcs and
|
||||||
not running_from_diff and
|
not running_from_diff and
|
||||||
(argument == filename))
|
(argument == filename))
|
||||||
return ((explicitly_provided or matches_filename_patterns) or
|
return (explicitly_provided or matches_filename_patterns or
|
||||||
is_stdin)
|
is_stdin or has_shebang(filename))
|
||||||
|
|
||||||
checks = self.checks.to_dictionary()
|
checks = self.checks.to_dictionary()
|
||||||
checkers = (
|
checkers = (
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,7 @@ def register_default_options(option_manager):
|
||||||
)
|
)
|
||||||
|
|
||||||
add_option(
|
add_option(
|
||||||
'--filename', metavar='patterns', default='*.py',
|
'--filename', metavar='patterns',
|
||||||
parse_from_config=True, comma_separated_list=True,
|
parse_from_config=True, comma_separated_list=True,
|
||||||
help='Only check for filenames matching the patterns in this comma-'
|
help='Only check for filenames matching the patterns in this comma-'
|
||||||
'separated list. (Default: %default)',
|
'separated list. (Default: %default)',
|
||||||
|
|
|
||||||
1
tests/fixtures/example-code/script
vendored
Normal file
1
tests/fixtures/example-code/script
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
"""Tests for the Manager object for FileCheckers."""
|
"""Tests for the Manager object for FileCheckers."""
|
||||||
import errno
|
import errno
|
||||||
|
import os
|
||||||
|
|
||||||
import mock
|
import mock
|
||||||
import pytest
|
import pytest
|
||||||
|
|
@ -70,3 +71,57 @@ def test_make_checkers():
|
||||||
|
|
||||||
for file_checker in manager.checkers:
|
for file_checker in manager.checkers:
|
||||||
assert file_checker.filename in files
|
assert file_checker.filename in files
|
||||||
|
|
||||||
|
|
||||||
|
def test_make_checkers_shebang():
|
||||||
|
"""Verify that extension-less files with a Python shebang are checked."""
|
||||||
|
style_guide = style_guide_mock(
|
||||||
|
filename=[],
|
||||||
|
exclude=[],
|
||||||
|
)
|
||||||
|
checkplugins = mock.Mock()
|
||||||
|
checkplugins.to_dictionary.return_value = {
|
||||||
|
'ast_plugins': [],
|
||||||
|
'logical_line_plugins': [],
|
||||||
|
'physical_line_plugins': [],
|
||||||
|
}
|
||||||
|
with mock.patch('flake8.checker.multiprocessing', None):
|
||||||
|
manager = checker.Manager(style_guide, [], checkplugins)
|
||||||
|
|
||||||
|
path = os.path.abspath(
|
||||||
|
os.path.join(os.path.dirname(__file__), '..', 'fixtures')
|
||||||
|
)
|
||||||
|
manager.make_checkers([path])
|
||||||
|
|
||||||
|
filenames = [
|
||||||
|
os.path.relpath(file_checker.filename, path)
|
||||||
|
for file_checker in manager.checkers
|
||||||
|
]
|
||||||
|
assert 'example-code/script' in filenames
|
||||||
|
|
||||||
|
|
||||||
|
def test_make_checkers_explicit_pattern_ignore_shebang():
|
||||||
|
"""Verify that shebangs are ignored when passing a pattern."""
|
||||||
|
style_guide = style_guide_mock(
|
||||||
|
filename=['*.py'],
|
||||||
|
exclude=[],
|
||||||
|
)
|
||||||
|
checkplugins = mock.Mock()
|
||||||
|
checkplugins.to_dictionary.return_value = {
|
||||||
|
'ast_plugins': [],
|
||||||
|
'logical_line_plugins': [],
|
||||||
|
'physical_line_plugins': [],
|
||||||
|
}
|
||||||
|
with mock.patch('flake8.checker.multiprocessing', None):
|
||||||
|
manager = checker.Manager(style_guide, [], checkplugins)
|
||||||
|
|
||||||
|
path = os.path.abspath(
|
||||||
|
os.path.join(os.path.dirname(__file__), '..', 'fixtures')
|
||||||
|
)
|
||||||
|
manager.make_checkers([path])
|
||||||
|
|
||||||
|
filenames = [
|
||||||
|
os.path.relpath(file_checker.filename, path)
|
||||||
|
for file_checker in manager.checkers
|
||||||
|
]
|
||||||
|
assert 'example-code/script' not in filenames
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue