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
|
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation; either version 2 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
"""Monkey patch compiler.transformer to fix line numbering bugs
:author: Sylvain Thenault
:copyright: 2003-2008 LOGILAB S.A. (Paris, FRANCE)
:contact: http://www.logilab.fr/ -- mailto:python-projects@logilab.org
:copyright: 2003-2008 Sylvain Thenault
:contact: mailto:thenault@gmail.com
"""
from types import TupleType
from compiler import transformer
from logilab.astng import nodes
def fromto_lineno(asttuple):
"""return the minimum and maximum line number of the given ast tuple"""
return from_lineno(asttuple), to_lineno(asttuple)
def from_lineno(asttuple):
"""return the minimum line number of the given ast tuple"""
if type(asttuple[1]) is TupleType:
return from_lineno(asttuple[1])
return asttuple[2]
def to_lineno(asttuple):
"""return the maximum line number of the given ast tuple"""
if type(asttuple[-1]) is TupleType:
return to_lineno(asttuple[-1])
return asttuple[2]
def fix_lineno(node, fromast, toast=None):
if 'fromlineno' in node.__dict__:
return node
#print 'fixing', id(node), id(node.__dict__), node.__dict__.keys(), repr(node)
if isinstance(node, nodes.Stmt):
node.fromlineno = from_lineno(fromast)#node.nodes[0].fromlineno
node.tolineno = node.nodes[-1].tolineno
return node
if toast is None:
node.fromlineno, node.tolineno = fromto_lineno(fromast)
else:
node.fromlineno, node.tolineno = from_lineno(fromast), to_lineno(toast)
#print 'fixed', id(node)
return node
BaseTransformer = transformer.Transformer
COORD_MAP = {
# if: test ':' suite ('elif' test ':' suite)* ['else' ':' suite]
'if': (0, 0),
# 'while' test ':' suite ['else' ':' suite]
'while': (0, 1),
# 'for' exprlist 'in' exprlist ':' suite ['else' ':' suite]
'for': (0, 3),
# 'try' ':' suite (except_clause ':' suite)+ ['else' ':' suite]
'try': (0, 0),
# | 'try' ':' suite 'finally' ':' suite
}
def fixlineno_wrap(function, stype):
def fixlineno_wrapper(self, nodelist):
node = function(self, nodelist)
idx1, idx2 = COORD_MAP.get(stype, (0, -1))
return fix_lineno(node, nodelist[idx1], nodelist[idx2])
return fixlineno_wrapper
class ASTNGTransformer(BaseTransformer):
"""ovverides transformer for a better source line number handling"""
def com_NEWLINE(self, *args):
# A ';' at the end of a line can make a NEWLINE token appear
# here, Render it harmless. (genc discards ('discard',
# ('const', xxxx)) Nodes)
lineno = args[0][1]
# don't put fromlineno/tolineno on Const None to mark it as dynamically
# added, without "physical" reference in the source
n = nodes.Discard(nodes.Const(None))
n.fromlineno = n.tolineno = lineno
return n
def com_node(self, node):
res = self._dispatch[node[0]](node[1:])
return fix_lineno(res, node)
def com_assign(self, node, assigning):
res = BaseTransformer.com_assign(self, node, assigning)
return fix_lineno(res, node)
def com_apply_trailer(self, primaryNode, nodelist):
node = BaseTransformer.com_apply_trailer(self, primaryNode, nodelist)
return fix_lineno(node, nodelist)
## def atom(self, nodelist):
## node = BaseTransformer.atom(self, nodelist)
## return fix_lineno(node, nodelist[0], nodelist[-1])
def funcdef(self, nodelist):
node = BaseTransformer.funcdef(self, nodelist)
# XXX decorators
return fix_lineno(node, nodelist[-5], nodelist[-3])
def classdef(self, nodelist):
node = BaseTransformer.classdef(self, nodelist)
return fix_lineno(node, nodelist[0], nodelist[-2])
# wrap *_stmt methods
for name in dir(BaseTransformer):
if name.endswith('_stmt') and not (name in ('com_stmt',
'com_append_stmt')
or name in ASTNGTransformer.__dict__):
setattr(BaseTransformer, name,
fixlineno_wrap(getattr(BaseTransformer, name), name[:-5]))
transformer.Transformer = ASTNGTransformer
|