Skip to content

Commit

Permalink
Issue #1172 (#1199)
Browse files Browse the repository at this point in the history
* Add ExecutableMismatchViolation #9

* Add logic for ExecutableMismatch

* Add WPS452 to test_noqa.py #5

* Remove dependency and related test

* Implement the full checking logic (#13)

* Fix linting problem (#14)

* Clean up code (#15)

Fix checking logic so that we don't thoroughly check every comment

* Fix permissions (#16)

* Make a new visitor to handle files with 0 comments (#17)

* Add tests for ExecutableMismatchViolation (#18)

* Add ExecutableMismatchViolation #9

* Add WPS452 to test_noqa.py #5

* Remove dependency and related test

* Add test resource files from flake8-executable

* Add tests for check_valid_shebang #7

* Implement the full checking logic (#13)

* Fix linting problem (#14)

* Clean up code (#15)

Fix checking logic so that we don't thoroughly check every comment

* Fix permissions (#16)

* Ignore failing test files

* Update test file to be compatible with the new visitor

* Fix linter issues

* Make a new visitor to handle files with 0 comments (#17)

* Exclude test files from linting

Co-authored-by: Hanzhang <48179160+hanzhsun@users.noreply.github.com>
Co-authored-by: jrutqvist <joar.rutqvist@gmail.com>
Co-authored-by: Gabriel Chang <Gc.chang95@gmail.com>

* Update CHANGELOG.md

* Moved test resources to fixtures to be ignored by autopep8

* Add integration test

* Refactor tests

* Make the requested changes
Use temporary files for testing

* Update noqa.py

* Make additional requested changes

* Fix small formatting and typing issues

* Update comments.py

* Update comments.py

Co-authored-by: hanzhsu <emily_1999@link.cuhk.edu.hk>
Co-authored-by: Gabriel Chang <Gc.chang95@gmail.com>
Co-authored-by: Hanzhang <48179160+hanzhsun@users.noreply.github.com>
Co-authored-by: jrutqvist <joar.rutqvist@gmail.com>
Co-authored-by: Nikita Sobolev <mail@sobolevn.me>
  • Loading branch information
6 people committed Mar 6, 2020
1 parent 6cecf90 commit cf3aef9
Show file tree
Hide file tree
Showing 14 changed files with 378 additions and 21 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ Semantic versioning in our case means:

### Features

- **Breaking**: removes `flake8-executable`, now using `WPS452` instead of `EXE001..EXE005`
- **Breaking**: removes `flake8-print`, now using `WPS421` instead of `T001`
- **Breaking**: removes `flake8-annotations-complexity`,
now using `WPS234` instead of `TAE002`
Expand Down
1 change: 0 additions & 1 deletion docs/pages/usage/violations/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ flake8-pep3101 `S001 <https://github.com/gforcada/flake8-pep3101
flake8-bandit `S100 - S710 <https://github.com/tylerwince/flake8-bandit>`_, see also original ``bandit`` `codes <https://bandit.readthedocs.io/en/latest/plugins/index.html#complete-test-plugin-listing>`_
flake8-debugger `T100 <https://github.com/JBKahn/flake8-debugger/blob/master/flake8_debugger.py>`_
flake8-rst-docstrings `RST201 - RST499 <https://github.com/peterjc/flake8-rst-docstrings>`_
flake8-executable `EXE001 - EXE005 <https://github.com/xuhdev/flake8-executable>`_
darglint `DAR001 - DAR501 <https://github.com/terrencepreilly/darglint#error-codes>`_
wemake-python-styleguide WPS, defined here
============================= ======
Expand Down
15 changes: 0 additions & 15 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,6 @@ flake8-eradicate = "^0.2"
flake8-bandit = "^2.1"
flake8-broken-line = "^0.1"
flake8-rst-docstrings = "^0.0.12"
flake8-executable = "^2.0"
pep8-naming = "^0.9.1"
darglint = "^1.1"

Expand Down
1 change: 0 additions & 1 deletion tests/fixtures/external_plugins.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
#!/usr/bin/env perl
from sys import *
import sys
from typing import List, Union, Dict
Expand Down
10 changes: 10 additions & 0 deletions tests/plugins/tokenize_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,13 @@ def factory(code: str):
lines = io.StringIO(dedent(code))
return list(tokenize.generate_tokens(lambda: next(lines)))
return factory


@pytest.fixture(scope='session')
def parse_file_tokens(parse_tokens):
"""Parses tokens from a file."""
def factory(filename: str):
with open(filename, 'r', encoding='utf-8') as test_file:
file_content = test_file.read()
return parse_tokens(file_content)
return factory
1 change: 1 addition & 0 deletions tests/test_checker/test_noqa.py
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,7 @@
'WPS450': 1,
'WPS451': 0, # defined in version specific table.
'WPS452': 1, # also defined in version specific table.
'WPS453': 0,

'WPS500': 1,
'WPS501': 1,
Expand Down
1 change: 0 additions & 1 deletion tests/test_plugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
'S101', # flake8-bandit
'T100', # flake8-debugger
'RST215', # flake8-rst-docstrings
'EXE003', # flake8-executable
'DAR101', # darglint
)

Expand Down
30 changes: 30 additions & 0 deletions tests/test_visitors/test_tokenize/test_comments/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# -*- coding: utf-8 -*-

from os import chmod

import pytest

TEMP_FOLDER = 'tmp'
MODE_EXECUTABLE = 0o755
MODE_NON_EXECUTABLE = 0o644


@pytest.fixture()
def make_file(tmp_path):
"""Fixture to make a temporary executable or non executable file."""
def factory(
filename: str,
file_content: str,
is_executable: bool,
) -> str:
temp_folder = tmp_path / TEMP_FOLDER
temp_folder.mkdir()
test_file = temp_folder / filename
file_mode = MODE_EXECUTABLE if is_executable else MODE_NON_EXECUTABLE

test_file.write_text(file_content)
chmod(test_file.as_posix(), file_mode)

return test_file.as_posix()

return factory
197 changes: 197 additions & 0 deletions tests/test_visitors/test_tokenize/test_comments/test_valid_shebang.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
# -*- coding: utf-8 -*-

import pytest

from wemake_python_styleguide.violations.best_practices import ShebangViolation
from wemake_python_styleguide.visitors.tokenize.comments import ShebangVisitor

# Correct

exe001_neg_shouldnt_be_executable = """
if __name__ == '__main__':
print('I am not executable.')
"""

exe001_neg_executable = False
exe001_neg_filename = 'exe001_neg.py'

exe002_neg_shouldnt_be_executable = """
def a_lib_function():
print('I am not executable.')
"""

exe002_neg_executable = False
exe002_neg_filename = 'exe002_neg.py'

exe003_neg_good_shebang = """#!/usr/bin/python3
if __name__ == '__main__':
print('I have a good shebang.')
"""

exe003_neg_executable = True
exe003_neg_filename = 'exe003_neg.py'

exe004_neg_no_space_before_shebang = """#!/usr/bin/python3
if __name__ == '__main__':
print('I do not have whitespace before shebang.')
"""

exe004_neg_executable = True
exe004_neg_filename = 'exe004_neg.py'

exe005_neg_nothing_before_shebang = """#!/usr/bin/python3
if __name__ == '__main__':
print('I do not have any blank or comment lines before shebang.')
"""

exe005_neg_executable = True
exe005_neg_filename = 'exe005_neg.py'

# Wrong

exe001_pos_should_be_executable = """#!/usr/bin/python
if __name__ == '__main__':
print('I should be executable.')
"""

exe001_pos_executable = False
exe001_pos_filename = 'exe001_pos.py'

exe002_pos_shouldnt_be_executable = """
def a_lib_function():
print("I shouldn't be executable.")
"""

exe002_pos_executable = True
exe002_pos_filename = 'exe002_pos.py'

exe003_pos_good_shebang = """#!/bin/bash
if __name__ == '__main__':
print('I have a wrong shebang.')
"""

exe003_pos_executable = True
exe003_pos_filename = 'exe003_pos.py'

exe004_pos_no_space_before_shebang = """ #!/usr/bin/python3
if __name__ == '__main__':
print('I have whitespace before shebang.')
"""

exe004_pos_executable = True
exe004_pos_filename = 'exe004_pos.py'

exe005_pos_nothing_before_shebang = """
#
#!/usr/bin/python3
if __name__ == '__main__':
print('I have blank and comment lines before shebang.')
"""

exe005_pos_executable = True
exe005_pos_filename = 'exe005_pos.py'


@pytest.mark.parametrize(('filename', 'file_content', 'executable'), [
(
exe001_neg_filename,
exe001_neg_shouldnt_be_executable,
exe001_neg_executable,
),
(
exe002_neg_filename,
exe002_neg_shouldnt_be_executable,
exe002_neg_executable,
),
(
exe003_neg_filename,
exe003_neg_good_shebang,
exe003_neg_executable,
),
(
exe004_neg_filename,
exe004_neg_no_space_before_shebang,
exe004_neg_executable,
),
(
exe005_neg_filename,
exe005_neg_nothing_before_shebang,
exe005_neg_executable,
),
])
def test_exe_negative(
make_file,
assert_errors,
parse_file_tokens,
default_options,
filename,
file_content,
executable,
):
"""Testing cases when no errors should be reported."""
path_to_file = make_file(filename, file_content, executable)
file_tokens = parse_file_tokens(path_to_file)

visitor = ShebangVisitor(
default_options,
filename=path_to_file,
file_tokens=file_tokens,
)
visitor.run()
assert_errors(visitor, [])


@pytest.mark.parametrize(('filename', 'file_content', 'executable'), [
(
exe001_pos_filename,
exe001_pos_should_be_executable,
exe001_pos_executable,
),
(
exe002_pos_filename,
exe002_pos_shouldnt_be_executable,
exe002_pos_executable,
),
(
exe003_pos_filename,
exe003_pos_good_shebang,
exe003_pos_executable,
),
(
exe004_pos_filename,
exe004_pos_no_space_before_shebang,
exe004_pos_executable,
),
(
exe005_pos_filename,
exe005_pos_nothing_before_shebang,
exe005_pos_executable,
),
])
def test_exe_posititve(
make_file,
assert_errors,
parse_file_tokens,
default_options,
filename,
file_content,
executable,
):
"""Testing cases when no errors should be reported."""
path_to_file = make_file(filename, file_content, executable)
file_tokens = parse_file_tokens(path_to_file)

visitor = ShebangVisitor(
default_options,
filename=path_to_file,
file_tokens=file_tokens,
)
visitor.run()
assert_errors(visitor, [ShebangViolation])
8 changes: 8 additions & 0 deletions wemake_python_styleguide/logic/system.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# -*- coding: utf-8 -*-

import os


def is_executable_file(filename: str) -> bool:
"""Checks if a file is executable."""
return os.access(filename, os.X_OK)
1 change: 1 addition & 0 deletions wemake_python_styleguide/presets/types/file_tokens.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#: Used to store all token related visitors to be later passed to checker:
PRESET: Final = (
comments.WrongCommentVisitor,
comments.ShebangVisitor,

syntax.WrongKeywordTokenVisitor,

Expand Down
38 changes: 38 additions & 0 deletions wemake_python_styleguide/violations/best_practices.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@
ProtectedModuleMemberViolation
PositionalOnlyArgumentsViolation
LoopControlFinallyViolation
ShebangViolation
Best practices
--------------
Expand Down Expand Up @@ -127,6 +128,7 @@
.. autoclass:: ProtectedModuleMemberViolation
.. autoclass:: PositionalOnlyArgumentsViolation
.. autoclass:: LoopControlFinallyViolation
.. autoclass:: ShebangViolation
"""

Expand Down Expand Up @@ -2068,3 +2070,39 @@ class LoopControlFinallyViolation(ASTViolation):

error_template = 'Found `break` or `continue` in `finally` block'
code = 452


@final
class ShebangViolation(SimpleViolation):
"""
Forbids to execute the file with shebang incorrectly set.
A violation is raised in these cases :
- Shebang is present but the file is not executable.
- The file is executable but no shebang is present.
- Shebang is present but does not contain "python".
- There is whitespace before shebang.
- There are blank or comment lines before shebang.
Reasoning:
Setting the shebang incorrectly causes executable mismatch.
Solution:
Ensure the shebang is present on the first line,
contains "python", and there is no whitespace before.
Example::
# Correct:
#!/usr/bin/env python
# Wrong:
#!/usr/bin/env
#!/usr/bin/env python
.. versionadded:: 0.14.0
"""

error_template = 'Found executable mismatch: {0}'
code = 453
Loading

0 comments on commit cf3aef9

Please sign in to comment.