]> git.saurik.com Git - wxWidgets.git/blame - wxPython/distrib/wx_conv.py
added tech note about writing unit tests
[wxWidgets.git] / wxPython / distrib / wx_conv.py
CommitLineData
1fded56b
RD
1"""
2Convert modules from the old 'from wxPython.wx import *' style to
3the new 'import wx' style.
4
5Author: dfh@forestfield.co.uk (David Hughes)
6
7
8This should probably go in the tools package and have a startup script
9like the others, but I don't think it's ready for prime-time yet. So
10just put it in distrib for now so it doesn't get lost...
11
12--Robin
13"""
14
15
16import os, sys, time
17import string
18t0 = time.time()
19
20
21import wx
22print 'length = ', len(wx._newnames)
23
24for key in ['true', 'True', 'false', 'False']: # use native Python not wx.<bool>
25 if key in wx._newnames:
26 del wx._newnames[key]
27
28import wx.calendar, wx.gizmos, wx.glcanvas, wx.grid, wx.help, wx.html, \
29 wx.htmlhelp, wx.iewin, wx.ogl, wx.stc, wx.wizard, wx.xrc
30import wx.lib
31##import wx.lib.activexwrapper, wx.lib.analogclock, wx.lib.anchors, \
32import wx.lib.activexwrapper, wx.lib.anchors, \
33 wx.lib.buttons, wx.lib.calendar, wx.lib.ClickableHtmlWindow, \
34 wx.lib.colourdb, wx.lib.colourselect, wx.lib.dialogs, \
35 wx.lib.ErrorDialogs, wx.lib.evtmgr, wx.lib.fancytext, \
36 wx.lib.filebrowsebutton, wx.lib.gridmovers, wx.lib.grids, \
37 wx.lib.imagebrowser, wx.lib.imageutils, wx.lib.infoframe, \
38 wx.lib.intctrl, wx.lib.layoutf, wx.lib.multisash, \
39 wx.lib.popupctl, wx.lib.printout, wx.lib.rcsizer, \
40 wx.lib.rightalign, wx.lib.sheet, wx.lib.stattext, \
41 wx.lib.throbber, wx.lib.timectrl, wx.lib.wxPlotCanvas, \
42 wx.lib.wxpTag
43import wx.lib.mixins, wx.lib.editor, wx.lib.colourchooser
44import wx.lib.mixins.grid, wx.lib.mixins.imagelist, \
45 wx.lib.mixins.listctrl
46## wx.lib.mixins.listctrl, wx.lib.mixins.rubberband
47import wx.lib.editor.editor, wx.lib.editor.images, wx.lib.editor.selection
48import wx.lib.colourchooser.canvas, wx.lib.colourchooser.intl, wx.lib.colourchooser.pycolourbox, \
49 wx.lib.colourchooser.pycolourchooser, wx.lib.colourchooser.pycolourslider, wx.lib.colourchooser.pypalette
50
51wxlist = []
52for kd in wx._newnames.items():
53 wxlist.append(kd)
54wxlist.sort()
55
56n = 0
57for item in wxlist:
58 n += 1
59## print n, item
60print 'length = ', len(wx._newnames)
61print 'imports completed in ', time.time()-t0, 'secs'
62
63base_path = 'G:/wxConvert/Test1'
64exclude_dir = ( 'wx', 'wx_cc', 'reportlab', 'sqlite') # MUST exclude wx if it is below base_dir, others optional
65
66other_subs = { 'true': 'True',
67 'false': 'False'
68 }
69
70punct = string.punctuation.replace('_','') # exclude single underscore
71punct = punct.replace('.','') # and period
72punct = punct.replace('*','') # and asterisk, all allowed in names
73punctable = string.maketrans(punct, ' '*len(punct)) # map punctuation characters to spaces
74numfiles = 0
75
76#----------------------------------------------------------------------------
77
78class AFile:
79 " file object - collection of properties relating to current instance"
80 def __init__(self, subsdict):
81 self.subsdict = subsdict.copy() # dictionary of universal and local substitutions to make
82 self.importing = [] # list of wx modules being imported
83
84#----------------------------------------------------------------------------
85
86def visit(noargs, thispath, contentlist):
87 """ Function is called by os walk for every directory in base_path,
88 including base_path itself. Contentlist is a list of files/dirs in thispath.
89 Wx conversion function is called for every qualifying file in list
90 """
91 path = thispath
92 base = 'something'
93 while base: # check if thispath or a parent is excluded
94 path, base = os.path.split(path)
95 if base in exclude_dir:
96 print 'Excluded:', thispath
97 return
98
99 for item in contentlist:
100 pathname = os.path.join(thispath, item)
101 if os.path.isfile(pathname) and pathname[-3:].lower() == '.py':
102 wxconvert(pathname)
103
104def wxconvert(pathname):
105 """ Scan each line of text in pathname. Replace each occurrence of any key in wx._newnames
106 dictionary with the content stored under that key
107 """
108 global numfiles
109 afile = AFile(other_subs)
110 infile = open(pathname, 'r')
111 linelist = infile.readlines()
112 infile.close()
113
114 lnum = 0
115 outlist = []
116 for line in linelist:
117 lnum += 1
118 tokenlist = line.replace('.__', ' ').translate(punctable).split() # split on white space and punctuation
119 line, status = checkimports(afile, line, tokenlist) # return line with 'import' modifications
120 if not status:
121 print 'Unable to convert line %d in %s' % (lnum, pathname)
122 break
123 else:
124 for key in afile.subsdict: # do other changes first
125 if line.find(key) >= 0:
126 line = line.replace(key, afile.subsdict[key])
127 for token in tokenlist: # change wx names
128 if token in wx._newnames:
129 candidate = wx._newnames[token]
130 module = candidate[:candidate.rfind('.')]
131 if module in afile.importing:
132 line = line.replace(token, candidate)
133 outlist.append(line)
134 else:
135 outfile = open(pathname, 'w')
136 outfile.writelines(outlist)
137 numfiles += 1
138 print 'Converted:', pathname
139 outfile.close()
140
141def checkimports(afile, line, tlist):
142 """ Scan tokenlist for wxPython import statement. Add to afile.subsdict any
143 name changes that are necessary for the rest of the current source file.
144 Add to afile.importing any new import modules
145 Return a tuple (status, newstring) -
146 line, possibly modified if an import statmeny
147 status: 0: unable to handle complexity, 1: OK
148 """
149 aline = line
150 if len(tlist) == 0: return (aline, 1)
151 indent = ''
152 for ch in line:
153 if ch in string.whitespace:
154 indent += ' '
155 else:
156 break
157
158 if tlist[0] == 'import': # import module [as name] [module [as name]...]
159 skip = False
160 for t in tlist[1:]:
161 if skip:
162 skip = False
163 elif t == 'as':
164 skip = True
165 elif t.startswith('wx'):
166 aline = aline.replace(t, rename_module(afile, t))
167 elif (tlist[0] == 'from' and
168 tlist[1] == 'wxPython' and
169 tlist[2] == 'import'): # from wxPython import module
170 if len(tlist) > 4: # ...[as name] [module [as name]...]
171 return ('', 0) # too complicated
172 module = rename_module(afile,tlist[-1])
173 aline = indent = 'import ' + module + '\n'
174 elif (tlist[0] == 'from' and
175 tlist[1].startswith('wxPython') and
176 tlist[2] == 'import'): # from module import ....
177 if tlist[-1] <> '*': # ...name [as name] [name [as name]...]
178 aline = aline.replace(tlist[1], rename_module(afile, tlist[1]))
179 skip = False
180 for t in tlist[3:]:
181 if skip:
182 skip = False
183 elif t == 'as':
184 skip = True
185 else:
186 elem = t.split('.')
187 if elem[-1].startswith('wx'): # remove wx prefix from last element of name
188 elem[-1] = elem[-1][2:]
189 afile.subsdict[t] = '.'.join(elem) # and apply to each occurrence in source
190 else: # from module import *
191 module = rename_module(afile,tlist[1])
192 aline = indent = 'import ' + module + '\n'
193 return (aline, 1)
194
195def rename_module(afile, t, type='A'):
196 """ Substitute wx for wxPython.wx or wx, and wx.foo.bar for wxPython.foo.bar in token
197 foo.bar => wx.foo.bar is also permitted (from wxPython import foo.bar)
198 """
199 if t in ['wx', 'wxPython.wx']:
200 module = 'wx'
201 elif t.startswith('wxPython'):
202 module = t.replace('wxPython', 'wx')
203 elif t.startswith('wx'):
204 module = t
205 else:
206 module = 'wx.' + t
207 if module not in afile.importing:
208 afile.importing.append(module)
209 return module
210
211def main():
212 " Convert every file in base_dir and all subdirectories except in exclude_dir list"
213
214 os.path.walk(base_path, visit, None)
215 print '%d files converted in %.2f seconds' % (numfiles, time.time() - t0)
216
217#----------------------------------------------------------------------------
218
219if __name__ == '__main__':
220 main()