Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 37c9351

Browse files
committed
Handle more syntax errors.
Invoke compiler.syntax.check() after building AST. If a SyntaxError occurs, print the error and exit without generating a .pyc file. Refactor code to use compiler.misc.set_filename() rather than passing filename argument around to each CodeGenerator instance.
1 parent 09392b7 commit 37c9351

4 files changed

Lines changed: 154 additions & 58 deletions

File tree

Lib/compiler/pycodegen.py

Lines changed: 32 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import types
99
from cStringIO import StringIO
1010

11-
from compiler import ast, parse, walk
11+
from compiler import ast, parse, walk, syntax
1212
from compiler import pyassem, misc, future, symbols
1313
from compiler.consts import SC_LOCAL, SC_GLOBAL, SC_FREE, SC_CELL
1414
from compiler.consts import CO_VARARGS, CO_VARKEYWORDS, CO_NEWLOCALS,\
@@ -41,17 +41,19 @@ def __init__(self):
4141
self.__super_init(self)
4242
self.loop = None
4343

44-
45-
4644
def compile(filename, display=0):
4745
f = open(filename)
4846
buf = f.read()
4947
f.close()
5048
mod = Module(buf, filename)
51-
mod.compile(display)
52-
f = open(filename + "c", "wb")
53-
mod.dump(f)
54-
f.close()
49+
try:
50+
mod.compile(display)
51+
except SyntaxError, err:
52+
print "SyntaxError:", err
53+
else:
54+
f = open(filename + "c", "wb")
55+
mod.dump(f)
56+
f.close()
5557

5658
class Module:
5759
def __init__(self, source, filename):
@@ -61,7 +63,9 @@ def __init__(self, source, filename):
6163

6264
def compile(self, display=0):
6365
tree = parse(self.source)
64-
gen = ModuleCodeGenerator(self.filename, tree)
66+
misc.set_filename(self.filename, tree)
67+
syntax.check(tree)
68+
gen = ModuleCodeGenerator(tree)
6569
if display:
6670
import pprint
6771
print pprint.pprint(tree)
@@ -149,12 +153,11 @@ class CodeGenerator:
149153
__initialized = None
150154
class_name = None # provide default for instance variable
151155

152-
def __init__(self, filename):
156+
def __init__(self):
153157
if self.__initialized is None:
154158
self.initClass()
155159
self.__class__.__initialized = 1
156160
self.checkClass()
157-
self.filename = filename
158161
self.locals = misc.Stack()
159162
self.setups = misc.Stack()
160163
self.curStack = 0
@@ -306,7 +309,7 @@ def visitLambda(self, node):
306309
self._visitFuncOrLambda(node, isLambda=1)
307310

308311
def _visitFuncOrLambda(self, node, isLambda=0):
309-
gen = self.FunctionGen(node, self.filename, self.scopes, isLambda,
312+
gen = self.FunctionGen(node, self.scopes, isLambda,
310313
self.class_name, self.get_module())
311314
walk(node.code, gen)
312315
gen.finish()
@@ -324,7 +327,7 @@ def _visitFuncOrLambda(self, node, isLambda=0):
324327
self.emit('MAKE_FUNCTION', len(node.defaults))
325328

326329
def visitClass(self, node):
327-
gen = self.ClassGen(node, self.filename, self.scopes,
330+
gen = self.ClassGen(node, self.scopes,
328331
self.get_module())
329332
if node.doc:
330333
self.emit('LOAD_CONST', node.doc)
@@ -430,14 +433,14 @@ def visitFor(self, node):
430433
def visitBreak(self, node):
431434
if not self.setups:
432435
raise SyntaxError, "'break' outside loop (%s, %d)" % \
433-
(self.filename, node.lineno)
436+
(node.filename, node.lineno)
434437
self.set_lineno(node)
435438
self.emit('BREAK_LOOP')
436439

437440
def visitContinue(self, node):
438441
if not self.setups:
439442
raise SyntaxError, "'continue' outside loop (%s, %d)" % \
440-
(self.filename, node.lineno)
443+
(node.filename, node.lineno)
441444
kind, block = self.setups.top()
442445
if kind == LOOP:
443446
self.set_lineno(node)
@@ -454,12 +457,12 @@ def visitContinue(self, node):
454457
break
455458
if kind != LOOP:
456459
raise SyntaxError, "'continue' outside loop (%s, %d)" % \
457-
(self.filename, node.lineno)
460+
(node.filename, node.lineno)
458461
self.emit('CONTINUE_LOOP', loop_block)
459462
self.nextBlock()
460463
elif kind == END_FINALLY:
461464
msg = "'continue' not allowed inside 'finally' clause (%s, %d)"
462-
raise SyntaxError, msg % (self.filename, node.lineno)
465+
raise SyntaxError, msg % (node.filename, node.lineno)
463466

464467
def visitTest(self, node, jump):
465468
end = self.newBlock()
@@ -1085,10 +1088,10 @@ class ModuleCodeGenerator(NestedScopeMixin, CodeGenerator):
10851088

10861089
scopes = None
10871090

1088-
def __init__(self, filename, tree):
1089-
self.graph = pyassem.PyFlowGraph("<module>", filename)
1091+
def __init__(self, tree):
1092+
self.graph = pyassem.PyFlowGraph("<module>", tree.filename)
10901093
self.futures = future.find_futures(tree)
1091-
self.__super_init(filename)
1094+
self.__super_init()
10921095
walk(tree, self)
10931096

10941097
def get_module(self):
@@ -1098,7 +1101,7 @@ class AbstractFunctionCode:
10981101
optimized = 1
10991102
lambdaCount = 0
11001103

1101-
def __init__(self, func, filename, scopes, isLambda, class_name, mod):
1104+
def __init__(self, func, scopes, isLambda, class_name, mod):
11021105
self.class_name = class_name
11031106
self.module = mod
11041107
if isLambda:
@@ -1108,10 +1111,10 @@ def __init__(self, func, filename, scopes, isLambda, class_name, mod):
11081111
else:
11091112
name = func.name
11101113
args, hasTupleArg = generateArgList(func.argnames)
1111-
self.graph = pyassem.PyFlowGraph(name, filename, args,
1114+
self.graph = pyassem.PyFlowGraph(name, func.filename, args,
11121115
optimized=1)
11131116
self.isLambda = isLambda
1114-
self.super_init(filename)
1117+
self.super_init()
11151118

11161119
if not isLambda and func.doc:
11171120
self.setDocstring(func.doc)
@@ -1162,10 +1165,10 @@ class FunctionCodeGenerator(NestedScopeMixin, AbstractFunctionCode,
11621165

11631166
__super_init = AbstractFunctionCode.__init__
11641167

1165-
def __init__(self, func, filename, scopes, isLambda, class_name, mod):
1168+
def __init__(self, func, scopes, isLambda, class_name, mod):
11661169
self.scopes = scopes
11671170
self.scope = scopes[func]
1168-
self.__super_init(func, filename, scopes, isLambda, class_name, mod)
1171+
self.__super_init(func, scopes, isLambda, class_name, mod)
11691172
self.graph.setFreeVars(self.scope.get_free_vars())
11701173
self.graph.setCellVars(self.scope.get_cell_vars())
11711174
if self.graph.checkFlag(CO_GENERATOR_ALLOWED):
@@ -1174,12 +1177,12 @@ def __init__(self, func, filename, scopes, isLambda, class_name, mod):
11741177

11751178
class AbstractClassCode:
11761179

1177-
def __init__(self, klass, filename, scopes, module):
1180+
def __init__(self, klass, scopes, module):
11781181
self.class_name = klass.name
11791182
self.module = module
1180-
self.graph = pyassem.PyFlowGraph(klass.name, filename,
1183+
self.graph = pyassem.PyFlowGraph(klass.name, klass.filename,
11811184
optimized=0, klass=1)
1182-
self.super_init(filename)
1185+
self.super_init()
11831186
lnf = walk(klass.code, self.NameFinder(), verbose=0)
11841187
self.locals.push(lnf.getLocals())
11851188
self.graph.setFlag(CO_NEWLOCALS)
@@ -1200,10 +1203,10 @@ class ClassCodeGenerator(NestedScopeMixin, AbstractClassCode, CodeGenerator):
12001203

12011204
__super_init = AbstractClassCode.__init__
12021205

1203-
def __init__(self, klass, filename, scopes, module):
1206+
def __init__(self, klass, scopes, module):
12041207
self.scopes = scopes
12051208
self.scope = scopes[klass]
1206-
self.__super_init(klass, filename, scopes, module)
1209+
self.__super_init(klass, scopes, module)
12071210
self.graph.setFreeVars(self.scope.get_free_vars())
12081211
self.graph.setCellVars(self.scope.get_cell_vars())
12091212
## self.graph.setFlag(CO_NESTED)

Lib/compiler/syntax.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
"""Check for errs in the AST.
2+
3+
The Python parser does not catch all syntax errors. Others, like
4+
assignments with invalid targets, are caught in the code generation
5+
phase.
6+
7+
The compiler package catches some errors in the transformer module.
8+
But it seems clearer to write checkers that use the AST to detect
9+
errors.
10+
"""
11+
12+
from compiler import ast, walk
13+
14+
def check(tree, multi=None):
15+
v = SyntaxErrorChecker(multi)
16+
walk(tree, v)
17+
return v.errors
18+
19+
class SyntaxErrorChecker:
20+
"""A visitor to find syntax errors in the AST."""
21+
22+
def __init__(self, multi=None):
23+
"""Create new visitor object.
24+
25+
If optional argument multi is not None, then print messages
26+
for each error rather than raising a SyntaxError for the
27+
first.
28+
"""
29+
self.multi = multi
30+
self.errors = 0
31+
32+
def error(self, node, msg):
33+
self.errors = self.errors + 1
34+
if self.multi is not None:
35+
print "%s:%s: %s" % (node.filename, node.lineno, msg)
36+
else:
37+
raise SyntaxError, "%s (%s:%s)" % (msg, node.filename, node.lineno)
38+
39+
def visitAssign(self, node):
40+
# the transformer module handles many of these
41+
for target in node.nodes:
42+
if isinstance(target, ast.AssList):
43+
if target.lineno is None:
44+
target.lineno = node.lineno
45+
self.error(target, "can't assign to list comprehension")

0 commit comments

Comments
 (0)