-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathlint
More file actions
executable file
·155 lines (118 loc) · 3.92 KB
/
lint
File metadata and controls
executable file
·155 lines (118 loc) · 3.92 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
#!/usr/bin/env python
import argparse
import os
import re
import sys
MAX_LINE_LENGTH = 88
SCALA_KEYWORD_SPACING_RULE_REGEX = (r"\s("
r"if|while|try|match|catch|do|else|for"
r")({|\()"
)
def log(s):
print(s)
sys.stdout.flush()
def loge(s):
sys.stderr.write(s + "\n")
sys.stderr.flush()
def abort(s):
loge(s)
sys.exit(1)
errors = []
def error(path, lineno, line, cursor, msg):
errors.append((path, lineno, line, cursor, msg))
class Linter(object):
def matches(self, filepath):
abort("not implemented")
def check(self, filepath):
abort("not implemented")
@staticmethod
def check_line_length(filepath, lineno, line):
if len(line) > MAX_LINE_LENGTH:
msg = "Line length {0}, max {1}.".format(len(line), MAX_LINE_LENGTH)
error(filepath, lineno, line, MAX_LINE_LENGTH, msg)
@staticmethod
def disallow_char(filepath, lineno, line, c, crep):
if line.find(c) != -1:
msg = "Character '{0}' is not allowed.".format(crep)
error(filepath, lineno, line, line.find(c), msg)
class PyLinter(object):
def matches(self, filepath):
return filepath.endswith(".py")
def check(self, filepath):
with open(filepath, "r") as f:
lineno = 0
for line in f:
Linter.check_line_length(filepath, lineno, line[:-1])
Linter.disallow_char(filepath, lineno, line, "\t", "\\t")
lineno += 1
class ScalaLinter(object):
def matches(self, filepath):
return filepath.endswith(".scala")
def check(self, filepath):
with open(filepath, "r") as f:
lineno = 0
inside_scaladoc = False
scaladoc_indentation = -1
for line in f:
Linter.check_line_length(filepath, lineno, line[:-1])
Linter.disallow_char(filepath, lineno, line, "\t", "\\t")
# Check Scaladoc indentation
if (inside_scaladoc):
asterisk_index = line.find("*")
expected_indentation = scaladoc_indentation + 1
if (asterisk_index != expected_indentation):
msg = "Expected * at offset {0} but found it at offset {1}".format(
expected_indentation, asterisk_index)
error(filepath, lineno, line, asterisk_index, msg)
if line.find("*/") != -1:
inside_scaladoc = False
else:
if re.match("\s*/\\*\\*", line):
scaladoc_indentation = line.find("/**")
if scaladoc_indentation != -1 and line.find("*/") == -1:
inside_scaladoc = True
lineno += 1
# Check that there is a space after each keyword.
keyword_spacing = re.search(SCALA_KEYWORD_SPACING_RULE_REGEX, line)
if keyword_spacing:
msg = "Keywords should be surrounded with an empty space."
error(filepath, lineno, line, keyword_spacing.start(), msg)
linters = [
PyLinter(),
ScalaLinter()
]
def format_error(error):
file, lineno, line, cursor, msg = error
loge(file + ":" + str(lineno) + ": " + msg)
loge(line)
loge(" " * cursor + "^")
def flush_errors():
for error in errors:
format_error(error)
if errors:
msg = "{0} lint error{1} found.".format(
len(errors), "" if len(errors) == 1 else "s")
abort(msg)
def lint(filepath, force=False):
try:
for linter in linters:
if force or linter.matches(filepath):
linter.check(filepath)
except:
log("While processing {}.".format(filepath))
log("Unexpected error: {}".format(sys.exc_info()[0]))
raise
def main(args):
parser = argparse.ArgumentParser(description="Run basic lint checks.")
parser.add_argument("-p", "--path", required=True,
help="Path to specific file to check, or directory to check recursively.")
args = parser.parse_args(args[1:])
if os.path.isfile(args.path):
lint(os.path.abspath(args.path), force=True)
else:
for root, dirs, files in os.walk(args.path):
for file in files:
lint(os.path.abspath(os.path.join(root, file)))
flush_errors()
if __name__ == "__main__":
main(sys.argv)