sources for utestconvert.py [rev. 38799]
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
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
import re
import sys
import parser
d={}
#  d is the dictionary of unittest changes, keyed to the old name
#  used by unittest.
#  d[old][0] is the new replacement function.
#  d[old][1] is the operator you will substitute, or '' if there is none.
#  d[old][2] is the possible number of arguments to the unittest
#  function.
# Old Unittest Name             new name         operator  # of args
d['assertRaises']           = ('raises',               '', ['Any'])
d['fail']                   = ('raise AssertionError', '', [0,1])
d['assert_']                = ('assert',               '', [1,2])
d['failIf']                 = ('assert not',           '', [1,2])
d['assertEqual']            = ('assert',            ' ==', [2,3])
d['failIfEqual']            = ('assert not',        ' ==', [2,3])
d['assertIn']               = ('assert',            ' in', [2,3])
d['assertNotIn']            = ('assert',            ' not in', [2,3])
d['assertNotEqual']         = ('assert',            ' !=', [2,3])
d['failUnlessEqual']        = ('assert',            ' ==', [2,3])
d['assertAlmostEqual']      = ('assert round',      ' ==', [2,3,4])
d['failIfAlmostEqual']      = ('assert not round',  ' ==', [2,3,4])
d['assertNotAlmostEqual']   = ('assert round',      ' !=', [2,3,4])
d['failUnlessAlmostEquals'] = ('assert round',      ' ==', [2,3,4])
#  the list of synonyms
d['failUnlessRaises']      = d['assertRaises']
d['failUnless']            = d['assert_']
d['assertEquals']          = d['assertEqual']
d['assertNotEquals']       = d['assertNotEqual']
d['assertAlmostEquals']    = d['assertAlmostEqual']
d['assertNotAlmostEquals'] = d['assertNotAlmostEqual']
# set up the regular expressions we will need
leading_spaces = re.compile(r'^(\s*)') # this never fails
pat = ''
for k in d.keys():  # this complicated pattern to match all unittests
    pat += '|' + r'^(\s*)' + 'self.' + k + r'\(' # \tself.whatever(
old_names = re.compile(pat[1:])
linesep='\n'        # nobody will really try to convert files not read
                    # in text mode, will they?
def blocksplitter(fp):
    '''split a file into blocks that are headed by functions to rename'''
    blocklist = []
    blockstring = ''
    for line in fp:
        interesting = old_names.match(line)
        if interesting :
            if blockstring:
                blocklist.append(blockstring)
                blockstring = line # reset the block
        else:
            blockstring += line
            
    blocklist.append(blockstring)
    return blocklist
def rewrite_utest(block):
    '''rewrite every block to use the new utest functions'''
    '''returns the rewritten unittest, unless it ran into problems,
       in which case it just returns the block unchanged.
    '''
    utest = old_names.match(block)
    if not utest:
        return block
    old = utest.group(0).lstrip()[5:-1] # the name we want to replace
    new = d[old][0] # the name of the replacement function
    op  = d[old][1] # the operator you will use , or '' if there is none.
    possible_args = d[old][2]  # a list of the number of arguments the
                               # unittest function could possibly take.
                
    if possible_args == ['Any']: # just rename assertRaises & friends
        return re.sub('self.'+old, new, block)
    message_pos = possible_args[-1]
    # the remaining unittests can have an optional message to print
    # when they fail.  It is always the last argument to the function.
    try:
        indent, argl, trailer = decompose_unittest(old, block)
    except SyntaxError: # but we couldn't parse it!
        return block
    
    argnum = len(argl)
    if argnum not in possible_args:
        # sanity check - this one isn't real either
        return block
    elif argnum == message_pos:
        message = argl[-1]
        argl = argl[:-1]
    else:
        message = None
    if argnum is 0 or (argnum is 1 and argnum is message_pos): #unittest fail()
        string = ''
        if message:
            message = ' ' + message
    elif message_pos is 4:  # assertAlmostEqual & friends
        try:
            pos = argl[2].lstrip()
        except IndexError:
            pos = '7' # default if none is specified
        string = '(%s -%s, %s)%s 0' % (argl[0], argl[1], pos, op )
    else: # assert_, assertEquals and all the rest
        string = ' ' + op.join(argl)
    if message:
        string = string + ',' + message
    return indent + new + string + trailer
def decompose_unittest(old, block):
    '''decompose the block into its component parts'''
    ''' returns indent, arglist, trailer 
        indent -- the indentation
        arglist -- the arguments to the unittest function
        trailer -- any extra junk after the closing paren, such as #commment
    '''
 
    indent = re.match(r'(\s*)', block).group()
    pat = re.search('self.' + old + r'\(', block)
    args, trailer = get_expr(block[pat.end():], ')')
    arglist = break_args(args, [])
    if arglist == ['']: # there weren't any
        return indent, [], trailer
    for i in range(len(arglist)):
        try:
            parser.expr(arglist[i].lstrip('\t '))
        except SyntaxError:
            if i == 0:
                arglist[i] = '(' + arglist[i] + ')'
            else:
                arglist[i] = ' (' + arglist[i] + ')'
    return indent, arglist, trailer
def break_args(args, arglist):
    '''recursively break a string into a list of arguments'''
    try:
        first, rest = get_expr(args, ',')
        if not rest:
            return arglist + [first]
        else:
            return [first] + break_args(rest, arglist)
    except SyntaxError:
        return arglist + [args]
def get_expr(s, char):
    '''split a string into an expression, and the rest of the string'''
    pos=[]
    for i in range(len(s)):
        if s[i] == char:
            pos.append(i)
    if pos == []:
        raise SyntaxError # we didn't find the expected char.  Ick.
     
    for p in pos:
        # make the python parser do the hard work of deciding which comma
        # splits the string into two expressions
        try:
            parser.expr('(' + s[:p] + ')')
            return s[:p], s[p+1:]
        except SyntaxError: # It's not an expression yet
            pass
    raise SyntaxError       # We never found anything that worked.
if __name__ == '__main__':
    import sys
    import py
    usage = "usage: %prog [-s [filename ...] | [-i | -c filename ...]]"
    optparser = py.compat.optparse.OptionParser(usage)
    def select_output (option, opt, value, optparser, **kw):
        if hasattr(optparser, 'output'):
            optparser.error(
                'Cannot combine -s -i and -c options. Use one only.')
        else:
            optparser.output = kw['output']
    optparser.add_option("-s", "--stdout", action="callback",
                         callback=select_output,
                         callback_kwargs={'output':'stdout'},
                         help="send your output to stdout")
    optparser.add_option("-i", "--inplace", action="callback",
                         callback=select_output,
                         callback_kwargs={'output':'inplace'},
                         help="overwrite files in place")
    optparser.add_option("-c", "--copy", action="callback",
                         callback=select_output,
                         callback_kwargs={'output':'copy'},
                         help="copy files ... fn.py --> fn_cp.py")
    options, args = optparser.parse_args()
    output = getattr(optparser, 'output', 'stdout')
    if output in ['inplace', 'copy'] and not args:
        optparser.error(
                '-i and -c option  require at least one filename')
    if not args:
        s = ''
        for block in blocksplitter(sys.stdin.read()):
            s += rewrite_utest(block)
        sys.stdout.write(s)
    else:
        for infilename in args: # no error checking to see if we can open, etc.
            infile = file(infilename)
            s = ''
            for block in blocksplitter(infile):
                s += rewrite_utest(block)
            if output == 'inplace':
                outfile = file(infilename, 'w+')
            elif output == 'copy': # yes, just go clobber any existing .cp
                outfile = file (infilename[:-3]+ '_cp.py', 'w+')
            else:
                outfile = sys.stdout
            outfile.write(s)