replace most calls to emit 'SET_LINENO' will call to method set_lineno
based on bug report by Neil Schemenauer
diff --git a/Lib/compiler/pycodegen.py b/Lib/compiler/pycodegen.py
index a697b55..b3223c1 100644
--- a/Lib/compiler/pycodegen.py
+++ b/Lib/compiler/pycodegen.py
@@ -105,6 +105,22 @@
         else:
             self.emit(prefix + '_GLOBAL', name)
 
+    def set_lineno(self, node):
+        """Emit SET_LINENO if node has lineno attribute
+
+        Returns true if SET_LINENO was emitted.
+
+        There are no rules for when an AST node should have a lineno
+        attribute.  The transformer and AST code need to be reviewed
+        and a consistent policy implemented and documented.  Until
+        then, this method works around missing line numbers.
+        """
+        lineno = getattr(node, 'lineno', None)
+        if lineno is not None:
+            self.emit('SET_LINENO', lineno)
+            return 1
+        return 0
+
     # The first few visitor methods handle nodes that generator new
     # code objects 
 
@@ -128,7 +144,7 @@
 	gen = FunctionCodeGenerator(node, self.filename, isLambda)
 	walk(node.code, gen)
 	gen.finish()
-	self.emit('SET_LINENO', node.lineno)
+        self.set_lineno(node)
         for default in node.defaults:
             self.visit(default)
         self.emit('LOAD_CONST', gen.getCode())
@@ -138,7 +154,7 @@
         gen = ClassCodeGenerator(node, self.filename)
         walk(node.code, gen)
         gen.finish()
-        self.emit('SET_LINENO', node.lineno)
+        self.set_lineno(node)
         self.emit('LOAD_CONST', node.name)
         for base in node.bases:
             self.visit(base)
@@ -158,8 +174,7 @@
 	numtests = len(node.tests)
 	for i in range(numtests):
 	    test, suite = node.tests[i]
-	    if hasattr(test, 'lineno'):
-		self.emit('SET_LINENO', test.lineno)
+            self.set_lineno(test)
 	    self.visit(test)
 ## 	    if i == numtests - 1 and not node.else_:
 ## 		nextTest = end
@@ -178,7 +193,7 @@
 	self.nextBlock(end)
 
     def visitWhile(self, node):
-	self.emit('SET_LINENO', node.lineno)
+        self.set_lineno(node)
 
 	loop = self.newBlock()
 	else_ = self.newBlock()
@@ -189,7 +204,7 @@
 	self.nextBlock(loop)
 	self.loops.push(loop)
 
-	self.emit('SET_LINENO', node.lineno)
+        self.set_lineno(node)
 	self.visit(node.test)
 	self.emit('JUMP_IF_FALSE', else_ or after)
 
@@ -212,12 +227,12 @@
 	after = self.newBlock()
         self.loops.push(start)
 
-        self.emit('SET_LINENO', node.lineno)
+        self.set_lineno(node)
 	self.emit('SETUP_LOOP', after)
         self.visit(node.list)
         self.visit(ast.Const(0))
 	self.nextBlock(start)
-        self.emit('SET_LINENO', node.lineno)
+        self.set_lineno(node)
         self.emit('FOR_LOOP', anchor)
         self.visit(node.assign)
         self.visit(node.body)
@@ -233,7 +248,7 @@
 	if not self.loops:
 	    raise SyntaxError, "'break' outside loop (%s, %d)" % \
 		  (self.filename, node.lineno)
-	self.emit('SET_LINENO', node.lineno)
+        self.set_lineno(node)
 	self.emit('BREAK_LOOP')
 
     def visitContinue(self, node):
@@ -241,7 +256,7 @@
             raise SyntaxError, "'continue' outside loop (%s, %d)" % \
 		  (self.filename, node.lineno)
         l = self.loops.top()
-        self.emit('SET_LINENO', node.lineno)
+        self.set_lineno(node)
         self.emit('JUMP_ABSOLUTE', l)
 	self.nextBlock()
 
@@ -291,7 +306,7 @@
 	# XXX would be interesting to implement this via a
 	# transformation of the AST before this stage
 	end = self.newBlock()
-	self.emit('SET_LINENO', node.lineno)
+        self.set_lineno(node)
         # XXX __debug__ and AssertionError appear to be special cases
         # -- they are always loaded as globals even if there are local
         # names.  I guess this is a sort of renaming op.
@@ -309,7 +324,7 @@
 	self.emit('POP_TOP')
 
     def visitRaise(self, node):
-        self.emit('SET_LINENO', node.lineno)
+        self.set_lineno(node)
         n = 0
         if node.expr1:
             self.visit(node.expr1)
@@ -329,7 +344,7 @@
             lElse = self.newBlock()
         else:
             lElse = end
-        self.emit('SET_LINENO', node.lineno)
+        self.set_lineno(node)
         self.emit('SETUP_EXCEPT', handlers)
         self.visit(node.body)
         self.emit('POP_BLOCK')
@@ -339,8 +354,7 @@
         last = len(node.handlers) - 1
         for i in range(len(node.handlers)):
             expr, target, body = node.handlers[i]
-            if hasattr(expr, 'lineno'):
-                self.emit('SET_LINENO', expr.lineno)
+            self.set_lineno(expr)
             if expr:
                 self.emit('DUP_TOP')
                 self.visit(expr)
@@ -368,7 +382,7 @@
     
     def visitTryFinally(self, node):
         final = self.newBlock()
-        self.emit('SET_LINENO', node.lineno)
+        self.set_lineno(node)
         self.emit('SETUP_FINALLY', final)
         self.visit(node.body)
         self.emit('POP_BLOCK')
@@ -402,16 +416,16 @@
         self.loadName(node.name)
         
     def visitPass(self, node):
-        self.emit('SET_LINENO', node.lineno)
+        self.set_lineno(node)
 
     def visitImport(self, node):
-        self.emit('SET_LINENO', node.lineno)
+        self.set_lineno(node)
         for name in node.names:
             self.emit('IMPORT_NAME', name)
             self.storeName(name)
 
     def visitFrom(self, node):
-        self.emit('SET_LINENO', node.lineno)
+        self.set_lineno(node)
         self.emit('IMPORT_NAME', node.modname)
         for name in node.names:
             if name == '*':
@@ -426,7 +440,7 @@
     # next five implement assignments
 
     def visitAssign(self, node):
-        self.emit('SET_LINENO', node.lineno)
+        self.set_lineno(node)
         self.visit(node.expr)
         dups = len(node.nodes) - 1
         for i in range(len(node.nodes)):
@@ -477,8 +491,7 @@
     def visitCallFunc(self, node):
         pos = 0
         kw = 0
-        if hasattr(node, 'lineno'):
-            self.emit('SET_LINENO', node.lineno)
+        self.set_lineno(node)
         self.visit(node.node)
         for arg in node.args:
             self.visit(arg)
@@ -496,7 +509,7 @@
         self.emit(opcode, kw << 8 | pos)
 
     def visitPrint(self, node):
-        self.emit('SET_LINENO', node.lineno)
+        self.set_lineno(node)
         for child in node.nodes:
             self.visit(child)
             self.emit('PRINT_ITEM')
@@ -506,7 +519,7 @@
         self.emit('PRINT_NEWLINE')
 
     def visitReturn(self, node):
-        self.emit('SET_LINENO', node.lineno)
+        self.set_lineno(node)
         self.visit(node.value)
         self.emit('RETURN_VALUE')
 
@@ -637,15 +650,13 @@
         self.emit('BUILD_SLICE', len(node.nodes))
 
     def visitDict(self, node):
-	# XXX is this a good general strategy?  could it be done 
-	# separately from the general visitor
-	lineno = getattr(node, 'lineno', None)
-	if lineno:
-	    self.emit('SET_LINENO', lineno)
+        lineno = getattr(node, 'lineno', None)
+        if lineno:
+            set.emit('SET_LINENO', lineno)
         self.emit('BUILD_MAP', 0)
         for k, v in node.items:
 	    lineno2 = getattr(node, 'lineno', None)
-	    if lineno != lineno2:
+            if lineno2 is not None and lineno != lineno2:
 		self.emit('SET_LINENO', lineno2)
 		lineno = lineno2
             self.emit('DUP_TOP')
@@ -687,7 +698,7 @@
 	    self.graph.setFlag(CO_VARARGS)
 	if func.kwargs:
 	    self.graph.setFlag(CO_VARKEYWORDS)
-        self.emit('SET_LINENO', func.lineno)
+        self.set_lineno(func)
         if hasTupleArg:
             self.generateArgUnpack(func.argnames)
 
diff --git a/Tools/compiler/compiler/pycodegen.py b/Tools/compiler/compiler/pycodegen.py
index a697b55..b3223c1 100644
--- a/Tools/compiler/compiler/pycodegen.py
+++ b/Tools/compiler/compiler/pycodegen.py
@@ -105,6 +105,22 @@
         else:
             self.emit(prefix + '_GLOBAL', name)
 
+    def set_lineno(self, node):
+        """Emit SET_LINENO if node has lineno attribute
+
+        Returns true if SET_LINENO was emitted.
+
+        There are no rules for when an AST node should have a lineno
+        attribute.  The transformer and AST code need to be reviewed
+        and a consistent policy implemented and documented.  Until
+        then, this method works around missing line numbers.
+        """
+        lineno = getattr(node, 'lineno', None)
+        if lineno is not None:
+            self.emit('SET_LINENO', lineno)
+            return 1
+        return 0
+
     # The first few visitor methods handle nodes that generator new
     # code objects 
 
@@ -128,7 +144,7 @@
 	gen = FunctionCodeGenerator(node, self.filename, isLambda)
 	walk(node.code, gen)
 	gen.finish()
-	self.emit('SET_LINENO', node.lineno)
+        self.set_lineno(node)
         for default in node.defaults:
             self.visit(default)
         self.emit('LOAD_CONST', gen.getCode())
@@ -138,7 +154,7 @@
         gen = ClassCodeGenerator(node, self.filename)
         walk(node.code, gen)
         gen.finish()
-        self.emit('SET_LINENO', node.lineno)
+        self.set_lineno(node)
         self.emit('LOAD_CONST', node.name)
         for base in node.bases:
             self.visit(base)
@@ -158,8 +174,7 @@
 	numtests = len(node.tests)
 	for i in range(numtests):
 	    test, suite = node.tests[i]
-	    if hasattr(test, 'lineno'):
-		self.emit('SET_LINENO', test.lineno)
+            self.set_lineno(test)
 	    self.visit(test)
 ## 	    if i == numtests - 1 and not node.else_:
 ## 		nextTest = end
@@ -178,7 +193,7 @@
 	self.nextBlock(end)
 
     def visitWhile(self, node):
-	self.emit('SET_LINENO', node.lineno)
+        self.set_lineno(node)
 
 	loop = self.newBlock()
 	else_ = self.newBlock()
@@ -189,7 +204,7 @@
 	self.nextBlock(loop)
 	self.loops.push(loop)
 
-	self.emit('SET_LINENO', node.lineno)
+        self.set_lineno(node)
 	self.visit(node.test)
 	self.emit('JUMP_IF_FALSE', else_ or after)
 
@@ -212,12 +227,12 @@
 	after = self.newBlock()
         self.loops.push(start)
 
-        self.emit('SET_LINENO', node.lineno)
+        self.set_lineno(node)
 	self.emit('SETUP_LOOP', after)
         self.visit(node.list)
         self.visit(ast.Const(0))
 	self.nextBlock(start)
-        self.emit('SET_LINENO', node.lineno)
+        self.set_lineno(node)
         self.emit('FOR_LOOP', anchor)
         self.visit(node.assign)
         self.visit(node.body)
@@ -233,7 +248,7 @@
 	if not self.loops:
 	    raise SyntaxError, "'break' outside loop (%s, %d)" % \
 		  (self.filename, node.lineno)
-	self.emit('SET_LINENO', node.lineno)
+        self.set_lineno(node)
 	self.emit('BREAK_LOOP')
 
     def visitContinue(self, node):
@@ -241,7 +256,7 @@
             raise SyntaxError, "'continue' outside loop (%s, %d)" % \
 		  (self.filename, node.lineno)
         l = self.loops.top()
-        self.emit('SET_LINENO', node.lineno)
+        self.set_lineno(node)
         self.emit('JUMP_ABSOLUTE', l)
 	self.nextBlock()
 
@@ -291,7 +306,7 @@
 	# XXX would be interesting to implement this via a
 	# transformation of the AST before this stage
 	end = self.newBlock()
-	self.emit('SET_LINENO', node.lineno)
+        self.set_lineno(node)
         # XXX __debug__ and AssertionError appear to be special cases
         # -- they are always loaded as globals even if there are local
         # names.  I guess this is a sort of renaming op.
@@ -309,7 +324,7 @@
 	self.emit('POP_TOP')
 
     def visitRaise(self, node):
-        self.emit('SET_LINENO', node.lineno)
+        self.set_lineno(node)
         n = 0
         if node.expr1:
             self.visit(node.expr1)
@@ -329,7 +344,7 @@
             lElse = self.newBlock()
         else:
             lElse = end
-        self.emit('SET_LINENO', node.lineno)
+        self.set_lineno(node)
         self.emit('SETUP_EXCEPT', handlers)
         self.visit(node.body)
         self.emit('POP_BLOCK')
@@ -339,8 +354,7 @@
         last = len(node.handlers) - 1
         for i in range(len(node.handlers)):
             expr, target, body = node.handlers[i]
-            if hasattr(expr, 'lineno'):
-                self.emit('SET_LINENO', expr.lineno)
+            self.set_lineno(expr)
             if expr:
                 self.emit('DUP_TOP')
                 self.visit(expr)
@@ -368,7 +382,7 @@
     
     def visitTryFinally(self, node):
         final = self.newBlock()
-        self.emit('SET_LINENO', node.lineno)
+        self.set_lineno(node)
         self.emit('SETUP_FINALLY', final)
         self.visit(node.body)
         self.emit('POP_BLOCK')
@@ -402,16 +416,16 @@
         self.loadName(node.name)
         
     def visitPass(self, node):
-        self.emit('SET_LINENO', node.lineno)
+        self.set_lineno(node)
 
     def visitImport(self, node):
-        self.emit('SET_LINENO', node.lineno)
+        self.set_lineno(node)
         for name in node.names:
             self.emit('IMPORT_NAME', name)
             self.storeName(name)
 
     def visitFrom(self, node):
-        self.emit('SET_LINENO', node.lineno)
+        self.set_lineno(node)
         self.emit('IMPORT_NAME', node.modname)
         for name in node.names:
             if name == '*':
@@ -426,7 +440,7 @@
     # next five implement assignments
 
     def visitAssign(self, node):
-        self.emit('SET_LINENO', node.lineno)
+        self.set_lineno(node)
         self.visit(node.expr)
         dups = len(node.nodes) - 1
         for i in range(len(node.nodes)):
@@ -477,8 +491,7 @@
     def visitCallFunc(self, node):
         pos = 0
         kw = 0
-        if hasattr(node, 'lineno'):
-            self.emit('SET_LINENO', node.lineno)
+        self.set_lineno(node)
         self.visit(node.node)
         for arg in node.args:
             self.visit(arg)
@@ -496,7 +509,7 @@
         self.emit(opcode, kw << 8 | pos)
 
     def visitPrint(self, node):
-        self.emit('SET_LINENO', node.lineno)
+        self.set_lineno(node)
         for child in node.nodes:
             self.visit(child)
             self.emit('PRINT_ITEM')
@@ -506,7 +519,7 @@
         self.emit('PRINT_NEWLINE')
 
     def visitReturn(self, node):
-        self.emit('SET_LINENO', node.lineno)
+        self.set_lineno(node)
         self.visit(node.value)
         self.emit('RETURN_VALUE')
 
@@ -637,15 +650,13 @@
         self.emit('BUILD_SLICE', len(node.nodes))
 
     def visitDict(self, node):
-	# XXX is this a good general strategy?  could it be done 
-	# separately from the general visitor
-	lineno = getattr(node, 'lineno', None)
-	if lineno:
-	    self.emit('SET_LINENO', lineno)
+        lineno = getattr(node, 'lineno', None)
+        if lineno:
+            set.emit('SET_LINENO', lineno)
         self.emit('BUILD_MAP', 0)
         for k, v in node.items:
 	    lineno2 = getattr(node, 'lineno', None)
-	    if lineno != lineno2:
+            if lineno2 is not None and lineno != lineno2:
 		self.emit('SET_LINENO', lineno2)
 		lineno = lineno2
             self.emit('DUP_TOP')
@@ -687,7 +698,7 @@
 	    self.graph.setFlag(CO_VARARGS)
 	if func.kwargs:
 	    self.graph.setFlag(CO_VARKEYWORDS)
-        self.emit('SET_LINENO', func.lineno)
+        self.set_lineno(func)
         if hasTupleArg:
             self.generateArgUnpack(func.argnames)