Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemathinc
GitHub Repository: sagemathinc/python-wasm
Path: blob/main/python/pylang/tools/lint.js
1396 views
1
/* vim:fileencoding=utf-8
2
*
3
* Copyright (C) 2015 Kovid Goyal <kovid at kovidgoyal.net>
4
*
5
* Distributed under terms of the BSD license
6
*/
7
"use strict"; /*jshint node:true */
8
9
var fs = require("fs");
10
var path = require("path");
11
var utils = require("./utils");
12
var colored = utils.colored;
13
14
import createCompiler from "./compiler";
15
const PyLang = createCompiler();
16
17
var WARN = 1,
18
ERROR = 2;
19
var MESSAGES = {
20
undef: 'undefined symbol: "{name}"',
21
"unused-import": '"{name}" is imported but not used',
22
"unused-local": '"{name}" is defined but not used',
23
"loop-shadowed":
24
'The loop variable "{name}" was previously used in this scope at line: {line}',
25
"extra-semicolon": "This semi-colon is not needed",
26
"eol-semicolon": "Semi-colons at the end of the line are unnecessary",
27
"func-in-branch":
28
"JavaScript in strict mode does not allow the definition of named functions/classes inside a branch such as an if/try/switch",
29
"syntax-err": "A syntax error caused compilation to abort",
30
"import-err": "An import error caused compilation to abort",
31
"def-after-use":
32
'The symbol "{name}" is defined (at line {line}) after it is used',
33
"dup-key":
34
'JavaScript in strict mode does not allow for duplicate keys ("{name}" is duplicated) in object mode',
35
"dup-method": "The method {name} was defined previously at line: {line}",
36
};
37
38
var BUILTINS = Object.create(null);
39
(
40
"this self window document chr ord iterator_symbol print len range dir" +
41
" eval undefined arguments abs max min enumerate pow callable reversed sum" +
42
" getattr isFinite setattr hasattr parseInt parseFloat options_object" +
43
" isNaN JSON Math list set list_wrap ρσ_modules require bool int bin" +
44
" float iter Error EvalError set_wrap RangeError ReferenceError SyntaxError" +
45
" str TypeError URIError Exception AssertionError IndexError AttributeError KeyError" +
46
" ValueError ZeroDivisionError map hex filter zip dict dict_wrap UnicodeDecodeError HTMLCollection" +
47
" NodeList alert console Node Symbol NamedNodeMap ρσ_eslice ρσ_delslice Number" +
48
" Boolean encodeURIComponent decodeURIComponent setTimeout setInterval" +
49
" setImmediate clearTimeout clearInterval clearImmediate requestAnimationFrame" +
50
" id repr sorted __name__ equals get_module ρσ_str jstype divmod NaN"
51
)
52
.split(" ")
53
.forEach(function (x) {
54
BUILTINS[x] = true;
55
});
56
57
Object.keys(PyLang.NATIVE_CLASSES).forEach(function (name) {
58
BUILTINS[name] = true;
59
});
60
var has_prop = Object.prototype.hasOwnProperty.call.bind(
61
Object.prototype.hasOwnProperty
62
);
63
64
if (!String.prototype.startsWith) {
65
String.prototype.startsWith = function (searchString, position) {
66
position = position || 0;
67
return this.indexOf(searchString, position) === position;
68
};
69
}
70
71
function cmp(a, b) {
72
return a < b ? -1 : a > b ? 1 : 0;
73
}
74
75
function parse_file(code, filename) {
76
return PyLang.parse(code, {
77
filename: filename,
78
basedir: path.dirname(filename),
79
libdir: path.dirname(filename),
80
for_linting: true,
81
});
82
}
83
84
function msg_from_node(filename, ident, name, node, level, line) {
85
name =
86
name || (node.name ? (node.name.name ? node.name.name : node.name) : "");
87
if (node instanceof PyLang.AST_Lambda && node.name) name = node.name.name;
88
var msg = MESSAGES[ident]
89
.replace("{name}", name || "")
90
.replace("{line}", line || "");
91
return {
92
filename: filename,
93
start_line: node.start ? node.start.line : undefined,
94
start_col: node.start ? node.start.col : undefined,
95
end_line: node.end ? node.end.line : undefined,
96
end_col: node.end ? node.end.col : undefined,
97
ident: ident,
98
message: msg,
99
level: level || ERROR,
100
name: name,
101
other_line: line,
102
};
103
}
104
105
function Binding(name, node, options) {
106
options = options || {};
107
this.node = node;
108
this.name = name;
109
this.is_import = !!options.is_import;
110
this.is_function = !!options.is_function;
111
this.is_func_arg = !!options.is_func_arg;
112
this.is_method = !!options.is_method;
113
114
this.is_loop = false;
115
this.used = false;
116
}
117
118
var merge = utils.merge;
119
120
function Scope(is_toplevel, parent_scope, filename, is_class) {
121
this.parent_scope = parent_scope;
122
this.is_toplevel = !!is_toplevel;
123
this.is_class = !!is_class;
124
this.bindings = {};
125
this.children = [];
126
this.shadowed = [];
127
this.undefined_references = {};
128
this.unused_bindings = {};
129
this.nonlocals = {};
130
this.defined_after_use = {};
131
this.seen_method_names = {};
132
this.methods = {};
133
134
this.add_binding = function (name, node, options) {
135
var already_bound = has_prop(this.bindings, name);
136
var b = new Binding(name, node, options);
137
if (already_bound) {
138
if (this.bindings[name].used) b.used = true;
139
this.shadowed.push([name, this.bindings[name], b]);
140
}
141
this.bindings[name] = b;
142
return b;
143
};
144
145
this.add_nonlocal = function (name) {
146
this.nonlocals[name] = true;
147
};
148
149
this.register_use = function (name, node) {
150
if (has_prop(this.bindings, name)) {
151
this.bindings[name].used = true;
152
} else {
153
this.undefined_references[name] = node;
154
}
155
};
156
157
this.finalize = function () {
158
// Find defined after use
159
Object.keys(this.undefined_references).forEach(function (name) {
160
if (has_prop(this.bindings, name) && !has_prop(this.nonlocals, name)) {
161
var b = this.bindings[name];
162
b.used = true;
163
if (!has_prop(this.defined_after_use, name)) {
164
this.defined_after_use[name] = [this.undefined_references[name], b];
165
}
166
delete this.undefined_references[name];
167
}
168
if (has_prop(this.methods, name)) delete this.undefined_references[name];
169
}, this);
170
171
// Find unused bindings
172
Object.keys(this.bindings).forEach(function (name) {
173
var b = this.bindings[name];
174
// Check if it is used in a descendant scope
175
var found = false;
176
this.for_descendants(function (scope) {
177
if (has_prop(scope.undefined_references, name)) {
178
found = true;
179
// Remove from childs' undefined references
180
delete scope.undefined_references[name];
181
} else if (has_prop(scope.nonlocals, name) && has_prop(scope.bindings, name)) found = true;
182
});
183
if (!found && !b.used && !b.is_loop)
184
// We deliberately ignore unused loop variables so as not to complain for the
185
// common idiom of using a for loop to repeat an action, without referring to the
186
// loop variable
187
this.unused_bindings[name] = b;
188
}, this);
189
};
190
191
this.for_descendants = function (func) {
192
this.children.forEach(function (child) {
193
func(child);
194
child.for_descendants(func);
195
});
196
};
197
198
this.messages = function () {
199
var ans = [];
200
201
Object.keys(this.undefined_references).forEach(function (name) {
202
if (!(this.is_toplevel && has_prop(this.nonlocals, name))) {
203
var node = this.undefined_references[name];
204
ans.push(msg_from_node(filename, "undef", name, node));
205
}
206
}, this);
207
208
Object.keys(this.unused_bindings).forEach(function (name) {
209
var b = this.unused_bindings[name];
210
if (b.is_import) {
211
ans.push(msg_from_node(filename, "unused-import", name, b.node));
212
} else if (
213
!this.is_toplevel &&
214
!this.is_class &&
215
!b.is_func_arg &&
216
!b.is_method &&
217
!has_prop(this.nonlocals, name)
218
) {
219
if (!name.startsWith("_")) {
220
ans.push(msg_from_node(filename, "unused-local", name, b.node));
221
}
222
}
223
}, this);
224
225
this.shadowed.forEach(function (x) {
226
var name = x[0],
227
first = x[1],
228
second = x[2];
229
if (second.is_loop && !first.is_loop) {
230
var line = first.node.start ? first.node.start.line : undefined;
231
ans.push(
232
msg_from_node(
233
filename,
234
"loop-shadowed",
235
name,
236
second.node,
237
ERROR,
238
line
239
)
240
);
241
}
242
});
243
244
Object.keys(this.defined_after_use).forEach(function (name) {
245
var use = this.defined_after_use[name][0],
246
binding = this.defined_after_use[name][1];
247
ans.push(
248
msg_from_node(
249
filename,
250
"def-after-use",
251
name,
252
use,
253
ERROR,
254
binding.node.start.line
255
)
256
);
257
}, this);
258
259
return ans;
260
};
261
}
262
263
function Linter(toplevel, filename, code, options) {
264
this.scopes = [];
265
this.walked_scopes = [];
266
this.current_node = null;
267
this.in_assign = false;
268
this.branches = [];
269
this.messages = [];
270
this.builtins = utils.merge(BUILTINS, options.builtins || {});
271
272
this.add_binding = function (name, binding_node) {
273
var scope = this.scopes[this.scopes.length - 1];
274
var node = this.current_node;
275
var options = {
276
is_import:
277
node instanceof PyLang.AST_Import ||
278
node instanceof PyLang.AST_ImportedVar,
279
is_function: node instanceof PyLang.AST_Lambda,
280
is_method: node instanceof PyLang.AST_Method,
281
is_func_arg: node instanceof PyLang.AST_SymbolFunarg,
282
};
283
return scope.add_binding(name, binding_node || node, options);
284
};
285
286
this.add_nonlocal = function (name) {
287
var scope = this.scopes[this.scopes.length - 1];
288
return scope.add_nonlocal(name);
289
};
290
291
this.register_use = function (name) {
292
var scope = this.scopes[this.scopes.length - 1];
293
var node = this.current_node;
294
return scope.register_use(name, node);
295
};
296
297
this.handle_import = function () {
298
var node = this.current_node;
299
if (!node.argnames) {
300
var name = node.alias ? node.alias.name : node.key.split(".", 1)[0];
301
this.add_binding(name, node.alias || node);
302
}
303
};
304
305
this.handle_imported_var = function () {
306
var node = this.current_node;
307
var name = node.alias ? node.alias.name : node.name;
308
this.add_binding(name);
309
};
310
311
this.handle_lambda = function () {
312
var node = this.current_node;
313
var name = node.name ? node.name.name : undefined;
314
var scope = this.scopes[this.scopes.length - 1];
315
if (this.branches.length && name) {
316
this.messages.push(
317
msg_from_node(filename, "func-in-branch", node.name, node)
318
);
319
}
320
if (name) {
321
if (node instanceof PyLang.AST_Method) {
322
scope.methods[name] = true;
323
if (has_prop(scope.seen_method_names, name)) {
324
if (!node.is_setter)
325
this.messages.push(
326
msg_from_node(
327
filename,
328
"dup-method",
329
node.name,
330
node,
331
WARN,
332
scope.seen_method_names[name]
333
)
334
);
335
} else scope.seen_method_names[name] = node.start.line;
336
} else this.add_binding(name);
337
}
338
};
339
340
this.handle_assign = function () {
341
var node = this.current_node;
342
343
var handle_destructured = function (self, flat) {
344
for (var i = 0; i < flat.length; i++) {
345
var cnode = flat[i];
346
if (cnode instanceof PyLang.AST_SymbolRef) {
347
self.current_node = cnode;
348
cnode.lint_visited = true;
349
self.add_binding(cnode.name);
350
self.current_node = node;
351
}
352
}
353
};
354
355
if (node.left instanceof PyLang.AST_SymbolRef) {
356
node.left.lint_visited = node.operator === "="; // Could be compound assignment like: +=
357
if (node.operator === "=") {
358
// Only create a binding if the operator is not
359
// a compound assignment operator
360
this.current_node = node.left;
361
this.add_binding(node.left.name);
362
this.current_node = node;
363
}
364
} else if (node.left instanceof PyLang.AST_Array) {
365
// destructuring assignment: a, b = 1, 2
366
var flat = node.left.flatten();
367
handle_destructured(this, node.left.flatten());
368
} else if (
369
node.left instanceof PyLang.AST_Seq &&
370
node.left.car instanceof PyLang.AST_SymbolRef
371
) {
372
handle_destructured(this, node.left.to_array());
373
}
374
};
375
376
this.handle_vardef = function () {
377
var node = this.current_node;
378
if (node.value) this.current_node = node.value;
379
if (node.name instanceof PyLang.AST_SymbolNonlocal) {
380
this.add_nonlocal(node.name.name);
381
} else {
382
this.add_binding(node.name.name, node.name);
383
}
384
this.current_node = node;
385
};
386
387
this.handle_symbol_ref = function () {
388
var node = this.current_node;
389
this.register_use(node.name);
390
};
391
392
this.handle_decorator = function () {
393
var node = this.current_node.expression;
394
if (
395
node instanceof PyLang.AST_SymbolRef &&
396
PyLang.compile_time_decorators.indexOf(node.name) != -1
397
)
398
node.link_visited = true;
399
};
400
401
this.handle_scope = function () {
402
var node = this.current_node;
403
var nscope = new Scope(
404
node instanceof PyLang.AST_Toplevel,
405
this.scopes[this.scopes.length - 1],
406
filename,
407
node instanceof PyLang.AST_Class
408
);
409
if (this.scopes.length)
410
this.scopes[this.scopes.length - 1].children.push(nscope);
411
this.scopes.push(nscope);
412
};
413
414
this.handle_symbol_funarg = function () {
415
// Arguments in a function definition
416
var node = this.current_node;
417
this.add_binding(node.name);
418
};
419
420
this.handle_comprehension = function () {
421
this.handle_scope(); // Comprehensions create their own scope
422
this.handle_for_in();
423
};
424
425
this.handle_for_in = function () {
426
var node = this.current_node;
427
if (node.init instanceof PyLang.AST_SymbolRef) {
428
this.add_binding(node.init.name).is_loop = true;
429
node.init.lint_visited = true;
430
} else if (node.init instanceof PyLang.AST_Array) {
431
// destructuring assignment: for a, b in []
432
for (var i = 0; i < node.init.elements.length; i++) {
433
var cnode = node.init.elements[i];
434
if (cnode instanceof PyLang.AST_Seq) cnode = cnode.to_array();
435
if (cnode instanceof PyLang.AST_SymbolRef) cnode = [cnode];
436
if (Array.isArray(cnode)) {
437
for (var j = 0; j < cnode.length; j++) {
438
var elem = cnode[j];
439
if (elem instanceof PyLang.AST_SymbolRef) {
440
this.current_node = elem;
441
elem.lint_visited = true;
442
this.add_binding(elem.name).is_loop = true;
443
this.current_node = node;
444
}
445
}
446
}
447
}
448
}
449
};
450
451
this.handle_for_js = function () {
452
var node = this.current_node;
453
var js = node.condition.value;
454
var statements = js.split(";");
455
var decl = statements[0].trim();
456
if (decl.startsWith("var ")) decl = decl.slice(4);
457
var self = this;
458
decl.split(",").forEach(function (part) {
459
var name = /^[a-zA-Z0-9_]+/.exec(part.trimLeft())[0];
460
self.add_binding(name);
461
});
462
};
463
464
this.handle_except = function () {
465
var node = this.current_node;
466
if (node.argname) {
467
this.add_binding(node.argname.name, node.argname);
468
}
469
};
470
471
this.handle_empty_statement = function () {
472
var node = this.current_node;
473
if (node.stype == ";") {
474
this.messages.push(
475
msg_from_node(filename, "extra-semicolon", ";", node, WARN)
476
);
477
}
478
};
479
480
this.handle_class = function () {
481
var node = this.current_node;
482
if (node.name) {
483
node.name.lint_visited = true;
484
this.add_binding(node.name.name, node.name);
485
}
486
};
487
488
this.handle_object_literal = function () {
489
var node = this.current_node;
490
var seen = {};
491
(node.properties || []).forEach(function (prop) {
492
if (prop.key instanceof PyLang.AST_Constant) {
493
var val = prop.key.value;
494
if (has_prop(seen, val))
495
this.messages.push(msg_from_node(filename, "dup-key", val, prop));
496
seen[val] = true;
497
}
498
}, this);
499
};
500
501
this.handle_call = function () {
502
var node = this.current_node;
503
if (node.args.kwargs)
504
node.args.kwargs.forEach(function (kw) {
505
kw[0].lint_visited = true;
506
});
507
};
508
509
this.handle_with_clause = function () {
510
var node = this.current_node;
511
if (node.alias) this.add_binding(node.alias.name);
512
};
513
514
this._visit = function (node, cont) {
515
if (node.lint_visited) return;
516
this.current_node = node;
517
var scope_count = this.scopes.length;
518
var branch_count = this.branches.length;
519
if (
520
node instanceof PyLang.AST_If ||
521
node instanceof PyLang.AST_Try ||
522
node instanceof PyLang.AST_Catch ||
523
node instanceof PyLang.AST_Except ||
524
node instanceof PyLang.AST_Else
525
) {
526
this.branches.push(1);
527
}
528
529
if (node instanceof PyLang.AST_Lambda) {
530
this.handle_lambda();
531
} else if (node instanceof PyLang.AST_Import) {
532
this.handle_import();
533
} else if (node instanceof PyLang.AST_ImportedVar) {
534
this.handle_imported_var();
535
} else if (node instanceof PyLang.AST_Class) {
536
this.handle_class();
537
} else if (node instanceof PyLang.AST_BaseCall) {
538
this.handle_call();
539
} else if (node instanceof PyLang.AST_Assign) {
540
this.handle_assign();
541
} else if (node instanceof PyLang.AST_VarDef) {
542
this.handle_vardef();
543
} else if (node instanceof PyLang.AST_SymbolRef) {
544
this.handle_symbol_ref();
545
} else if (node instanceof PyLang.AST_Decorator) {
546
this.handle_decorator();
547
} else if (node instanceof PyLang.AST_SymbolFunarg) {
548
this.handle_symbol_funarg();
549
} else if (node instanceof PyLang.AST_ListComprehension) {
550
this.handle_comprehension();
551
} else if (node instanceof PyLang.AST_ForIn) {
552
this.handle_for_in();
553
} else if (node instanceof PyLang.AST_ForJS) {
554
this.handle_for_js();
555
} else if (node instanceof PyLang.AST_Except) {
556
this.handle_except();
557
} else if (node instanceof PyLang.AST_EmptyStatement) {
558
this.handle_empty_statement();
559
} else if (node instanceof PyLang.AST_WithClause) {
560
this.handle_with_clause();
561
} else if (node instanceof PyLang.AST_Object) {
562
this.handle_object_literal();
563
}
564
565
if (node instanceof PyLang.AST_Scope) {
566
this.handle_scope();
567
}
568
569
if (cont !== undefined) cont();
570
571
if (this.scopes.length > scope_count) {
572
this.scopes[this.scopes.length - 1].finalize();
573
this.walked_scopes.push(this.scopes.pop());
574
}
575
576
if (this.branches.length > branch_count) this.branches.pop();
577
};
578
579
this.resolve = function () {
580
var messages = this.messages;
581
var line_filters = {};
582
583
code.split("\n").forEach(function (line, num) {
584
line = line.trimRight();
585
num++;
586
if (line[line.length - 1] === ";") {
587
var ident = "eol-semicolon";
588
messages.push({
589
filename: filename,
590
ident: ident,
591
message: MESSAGES[ident],
592
level: WARN,
593
name: ";",
594
start_line: num,
595
start_col: line.lastIndexOf(";"),
596
});
597
}
598
var parts = line.split("#");
599
var last = parts[parts.length - 1],
600
filters;
601
if (last && last.trimLeft().substr(0, 4).toLowerCase() === "noqa") {
602
parts = last.split(":").slice(1);
603
if (parts.length) {
604
filters = {};
605
parts = parts[0].split(",");
606
for (var i = 0; i < parts.length; i++)
607
filters[parts[i].trim()] = true;
608
} else filters = MESSAGES;
609
}
610
if (filters) line_filters[num] = filters;
611
});
612
613
this.walked_scopes.forEach(function (scope) {
614
messages = messages.concat(scope.messages());
615
});
616
var noqa = options.noqa || {};
617
messages = messages.filter(function (msg) {
618
var ignore =
619
msg.start_line !== undefined &&
620
has_prop(line_filters, msg.start_line) &&
621
has_prop(line_filters[msg.start_line], msg.ident);
622
var filter = has_prop(noqa, msg.ident);
623
return (
624
!ignore &&
625
!filter &&
626
(msg.ident != "undef" || !has_prop(this.builtins, msg.name))
627
);
628
}, this);
629
messages.sort(function (a, b) {
630
return cmp(a.start_line, b.start_line) || cmp(a.start_col, b.start_col_);
631
});
632
return messages;
633
};
634
}
635
636
function lint_code(code, options) {
637
options = options || {};
638
var reportcb =
639
{ json: cli_json_report, vim: cli_vim_report, undef: cli_undef_report }[
640
options.errorformat
641
] ||
642
options.report ||
643
cli_report;
644
var filename = options.filename || "<eval>";
645
var toplevel, messages;
646
var lines = code.split("\n"); // Can be used (in the future) to display extract from code corresponding to error location
647
648
try {
649
toplevel = parse_file(code, filename);
650
} catch (e) {
651
if (e instanceof PyLang.ImportError) {
652
messages = [
653
{
654
filename: filename,
655
start_line: e.line,
656
start_col: e.col,
657
level: ERROR,
658
ident: "import-err",
659
message: e.message,
660
},
661
];
662
} else if (e instanceof PyLang.SyntaxError) {
663
messages = [
664
{
665
filename: filename,
666
start_line: e.line,
667
start_col: e.col,
668
level: ERROR,
669
ident: "syntax-err",
670
message: e.message,
671
},
672
];
673
} else throw e;
674
}
675
676
if (toplevel) {
677
var linter = new Linter(toplevel, filename, code, options);
678
toplevel.walk(linter);
679
messages = linter.resolve();
680
}
681
messages.forEach(function (msg, i) {
682
msg.code_lines = lines;
683
reportcb(msg, i, messages);
684
});
685
return messages;
686
}
687
688
// CLI {{{
689
690
function read_whole_file(filename, cb) {
691
if (!filename || filename === "-") {
692
var chunks = [];
693
process.stdin.setEncoding("utf-8");
694
process.stdin
695
.on("data", function (chunk) {
696
chunks.push(chunk);
697
})
698
.on("end", function () {
699
cb(null, chunks.join(""));
700
});
701
process.openStdin();
702
} else {
703
fs.readFile(filename, "utf-8", cb);
704
}
705
}
706
707
function cli_report(r, i, messages) {
708
var parts = [];
709
function push(x, color) {
710
parts.push(x === undefined ? "" : colored(x.toString(), color));
711
}
712
push(r.filename);
713
push(r.level === WARN ? "WARN" : "ERR", r.level === WARN ? "yellow" : "red");
714
push(r.start_line, "green");
715
push(r.start_col === undefined ? "" : r.start_col + 1);
716
console.log(
717
parts.join(":") + ":" + r.message + colored(" [" + r.ident + "]", "green")
718
);
719
if (i < messages.length - 1) console.log();
720
}
721
722
var undef_buf = {};
723
724
function cli_undef_report(r, i, messages) {
725
if (r.ident == "undef" && r.name) undef_buf[r.name] = true;
726
if (i == messages.length - 1)
727
console.log(Object.keys(undef_buf).sort().join(", "));
728
}
729
730
function cli_json_report(r, i, messages) {
731
var j = {};
732
Object.keys(r).forEach(function (key) {
733
var val = r[key];
734
if (val !== undefined && key != "code_lines") {
735
if (key === "level") val = val === WARN ? "WARN" : "ERR";
736
j[key] = val;
737
}
738
});
739
if (i === 0) console.log("[");
740
console.log(JSON.stringify(j, null, 2));
741
console.log(i < messages.length - 1 ? "," : "]");
742
}
743
744
function cli_vim_report(r) {
745
var parts = [];
746
parts.push(r.filename);
747
parts.push(r.start_line || 0);
748
parts.push(r.start_col === undefined ? 0 : r.start_col + 1);
749
parts.push(r.level === WARN ? "W" : "E");
750
parts.push(r.name || "");
751
parts.push(r.message + " [" + r.ident + "]");
752
console.log(parts.join(":"));
753
}
754
755
var ini_cache = {};
756
757
function get_ini(toplevel_dir) {
758
if (has_prop(ini_cache, toplevel_dir)) return ini_cache[toplevel_dir];
759
var rl = require("./ini").read_config(toplevel_dir).rapydscript || {};
760
ini_cache[toplevel_dir] = rl;
761
return rl;
762
}
763
764
module.exports.cli = function (argv, base_path, src_path, lib_path) {
765
var files = argv.files.slice();
766
var num_of_files = files.length || 1;
767
var read_config = require("./ini");
768
769
if (argv.noqa_list) {
770
Object.keys(MESSAGES).forEach(function (ident) {
771
var i = (ident + utils.repeat(" ", 20)).slice(0, 20);
772
var h = utils.wrap(MESSAGES[ident].split("\n"), 59);
773
console.log(i + h[0]);
774
h.slice(1).forEach(function (t) {
775
console.log(utils.repeat(" ", 20) + t);
776
});
777
console.log();
778
});
779
process.exit(0);
780
}
781
782
if (
783
files.filter(function (el) {
784
return el === "-";
785
}).length > 1
786
) {
787
console.error(
788
"ERROR: Can read a single file from STDIN (two or more dashes specified)"
789
);
790
process.exit(1);
791
}
792
793
var all_ok = true;
794
var builtins = {};
795
var noqa = {};
796
if (argv.globals)
797
argv.globals.split(",").forEach(function (sym) {
798
builtins[sym] = true;
799
});
800
if (argv.noqa)
801
argv.noqa.split(",").forEach(function (sym) {
802
noqa[sym] = true;
803
});
804
805
function path_for_filename(x) {
806
return x === "-" ? argv.stdin_filename : x;
807
}
808
809
function lint_single_file(err, code) {
810
var output,
811
final_builtins = merge(builtins),
812
final_noqa = merge(noqa),
813
rl;
814
if (err) {
815
console.error("ERROR: can't read file: " + files[0]);
816
process.exit(1);
817
}
818
819
// Read setup.cfg
820
rl = get_ini(path.dirname(path_for_filename(files[0])));
821
var g = {};
822
(rl.globals || rl.builtins || "").split(",").forEach(function (x) {
823
g[x.trim()] = true;
824
});
825
final_builtins = merge(final_builtins, g);
826
g = {};
827
(rl.noqa || "").split(",").forEach(function (x) {
828
g[x.trim()] = true;
829
});
830
final_noqa = merge(final_noqa, g);
831
832
// Look for # globals: or # noqa: in the first few lines of the file
833
code.split("\n", 20).forEach(function (line) {
834
var lq = line.replace(/\s+/g, "");
835
if (lq.startsWith("#globals:")) {
836
(lq.split(":", 2)[1] || "").split(",").forEach(function (item) {
837
final_builtins[item] = true;
838
});
839
} else if (lq.startsWith("#noqa:")) {
840
(lq.split(":", 2)[1] || "").split(",").forEach(function (item) {
841
final_noqa[item] = true;
842
});
843
}
844
});
845
846
// Lint!
847
if (
848
lint_code(code, {
849
filename: path_for_filename(files[0]),
850
builtins: final_builtins,
851
noqa: final_noqa,
852
errorformat: argv.errorformat || false,
853
}).length
854
)
855
all_ok = false;
856
857
files = files.slice(1);
858
if (files.length) {
859
setImmediate(read_whole_file, files[0], lint_single_file);
860
return;
861
} else process.exit(all_ok ? 0 : 1);
862
}
863
864
setImmediate(read_whole_file, files[0], lint_single_file);
865
};
866
867
module.exports.lint_code = lint_code;
868
module.exports.WARN = WARN;
869
module.exports.ERROR = ERROR;
870
module.exports.MESSAGES = MESSAGES;
871
// }}}
872
873