Skip to content

Commit

Permalink
WIP: Lint script from stdin (#240)
Browse files Browse the repository at this point in the history
  • Loading branch information
RianFuro authored and Kuniwak committed Jun 18, 2018
1 parent 172b110 commit cac0cf7
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 12 deletions.
5 changes: 5 additions & 0 deletions test/unit/vint/linting/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,5 +56,10 @@ def test_start_with_both_calid_invalid_file_paths(self):
self.assertExitWithFailure(argv)


@mock.patch('sys.stdin', open('test/fixture/cli/valid1.vim'))
def test_passing_code_to_stdin_lints_the_code_from_stdin(self):
argv = ['bin/vint', '-']
self.assertExitWithSuccess(argv)

if __name__ == '__main__':
unittest.main()
23 changes: 15 additions & 8 deletions vint/linting/cli.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import sys
from argparse import ArgumentParser
from pathlib import PosixPath
import pkg_resources
import logging

Expand All @@ -15,7 +16,6 @@
from vint.linting.formatter.json_formatter import JSONFormatter
from vint.linting.formatter.statistic_formatter import StatisticFormatter


class CLI(object):
def start(self):
env = self._build_env(sys.argv)
Expand Down Expand Up @@ -44,11 +44,12 @@ def _validate(self, env):
parser.print_help()
parser.exit(status=1)

for path_to_lint in paths_to_lint:
if not path_to_lint.exists() or not path_to_lint.is_file():
logging.error('no such file or directory: `{path}`'.format(
path=str(path_to_lint)))
parser.exit(status=1)
if not self._should_read_from_stdin(env):
for path_to_lint in paths_to_lint:
if not path_to_lint.exists() or not path_to_lint.is_file():
logging.error('no such file or directory: `{path}`'.format(
path=str(path_to_lint)))
parser.exit(status=1)


def _build_config_dict(self, env):
Expand Down Expand Up @@ -116,11 +117,17 @@ def _lint_all(self, env, config_dict):
violations = []
linter = self._build_linter(config_dict)

for file_path in paths_to_lint:
violations += linter.lint_file(file_path)
if self._should_read_from_stdin(env):
violations += linter.lint_text(sys.stdin.read())
else:
for file_path in paths_to_lint:
violations += linter.lint_file(file_path)

return violations

def _should_read_from_stdin(self, env):
return len(env['file_paths']) == 1 and PosixPath('-') in env['file_paths']


def _get_formatter(self, config_dict):
if 'cmdargs' not in config_dict:
Expand Down
31 changes: 27 additions & 4 deletions vint/linting/linter.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,11 +112,31 @@ def lint_file(self, path):
decoding_error = self._create_decoding_error(path, str(exception))
return [decoding_error]

self._traverse(root_ast, path)

return self._violations


def lint_text(self, text):
self._log_file_path_to_lint('stdin')

try:
root_ast = self._parser.parse(text)
except vimlparser.VimLParserException as exception:
parse_error = self._create_parse_error('stdin', str(exception))
return [parse_error]
except EncodingDetectionError as exception:
decoding_error = self._create_decoding_error('stdin', str(exception))
return [decoding_error]

self._traverse(root_ast, 'stdin')

return self._violations

self._violations = []
self._update_listeners_map()

# Given root AST to makepolicy flexibility
def _traverse(self, root_ast, path):
self._prepare_for_traversal()

lint_context = {
'path': path,
'root_node': root_ast,
Expand All @@ -129,7 +149,10 @@ def lint_file(self, path):
on_enter=lambda node: self._handle_enter(node, lint_context),
on_leave=lambda node: self._handle_leave(node, lint_context))

return self._violations

def _prepare_for_traversal(self):
self._violations = []
self._update_listeners_map()


def _handle_enter(self, node, lint_context):
Expand Down
21 changes: 21 additions & 0 deletions vint/linting/policy/prohibit_missing_scriptencoding.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import chardet
import sys

from vint.ast.node_type import NodeType
from vint.ast.traversing import traverse, SKIP_CHILDREN
Expand Down Expand Up @@ -49,6 +50,26 @@ def _check_scriptencoding(self, node):


def _check_script_has_multibyte_char(self, lint_context):
if self._is_from_stdin(lint_context):
return self._check_stdin_has_multibyte_char()
else:
return self._check_file_has_multibyte_char(lint_context)


def _is_from_stdin(self, lint_context):
return lint_context['path'] == 'stdin'


def _check_stdin_has_multibyte_char(self):
sys.stdin.seek(0)

try:
sys.stdin.read().encode('ascii')
return False
except UnicodeError:
return True

def _check_file_has_multibyte_char(self, lint_context):
# TODO: Use cache to make performance efficiency
with lint_context['path'].open(mode='br') as f:
byte_seq = f.read()
Expand Down

0 comments on commit cac0cf7

Please sign in to comment.