blob: c666ee005617f404395473eae733875980530987 [file] [log] [blame]
Guido van Rossum0af9a281992-01-01 18:38:21 +00001#! /ufs/guido/bin/sgi/python
2#! /usr/local/python
3
4# Fix Python source files to use the new equality test operator,
5# i.e.,
6# if x == y: ...
7# instead of
8# if x = y: ...
9#
10# Command line arguments are files or directories to be processed.
11# Directories are searched recursively for files whose name looks
12# like a python module.
13# Symbolic links are always ignored (except as explicit directory
14# arguments). Of course, the original file is kept as a back-up
15# (with a "~" attached to its name).
16#
17# Changes made are reported to stdout in a diff-like format.
18#
19# Undoubtedly you can do this using find and sed or perl, but this is
20# a nice example of Python code that recurses down a directory tree
21# and uses regular expressions. Also note several subtleties like
22# preserving the file's mode and avoiding to even write a temp file
23# when no changes are needed for a file.
24#
25# NB: by changing only the function fixline() you can turn this
26# into a program for a different change to Python programs...
27
28import sys
29import regex
30import posix
31import path
32from stat import *
33
34err = sys.stderr.write
35dbg = err
36rep = sys.stdout.write
37
38def main():
39 bad = 0
40 if not sys.argv[1:]: # No arguments
41 err('usage: ' + argv[0] + ' file-or-directory ...\n')
42 sys.exit(2)
43 for arg in sys.argv[1:]:
44 if path.isdir(arg):
45 if recursedown(arg): bad = 1
46 elif path.islink(arg):
47 err(arg + ': will not process symbolic links\n')
48 bad = 1
49 else:
50 if fix(arg): bad = 1
51 sys.exit(bad)
52
53ispythonprog = regex.compile('^[a-zA-Z0-9_]+\.py$')
54def ispython(name):
55 return ispythonprog.match(name) >= 0
56
57def recursedown(dirname):
58 dbg('recursedown(' + `dirname` + ')\n')
59 bad = 0
60 try:
61 names = posix.listdir(dirname)
62 except posix.error, msg:
63 err(dirname + ': cannot list directory: ' + `msg` + '\n')
64 return 1
65 names.sort()
66 subdirs = []
67 for name in names:
68 if name in ('.', '..'): continue
69 fullname = path.join(dirname, name)
70 if path.islink(fullname): pass
71 elif path.isdir(fullname):
72 subdirs.append(fullname)
73 elif ispython(name):
74 if fix(fullname): bad = 1
75 for fullname in subdirs:
76 if recursedown(fullname): bad = 1
77 return bad
78
79def fix(filename):
80 dbg('fix(' + `filename` + ')\n')
81 try:
82 f = open(filename, 'r')
83 except IOError, msg:
84 err(filename + ': cannot open: ' + `msg` + '\n')
85 return 1
86 head, tail = path.split(filename)
87 tempname = path.join(head, '@' + tail)
88 g = None
89 # If we find a match, we rewind the file and start over but
90 # now copy everything to a temp file.
91 lineno = 0
92 while 1:
93 line = f.readline()
94 if not line: break
95 lineno = lineno + 1
96 while line[-2:] == '\\\n':
97 nextline = f.readline()
98 if not nextline: break
99 line = line + nextline
100 lineno = lineno + 1
101 newline = fixline(line)
102 if newline != line:
103 if g is None:
104 try:
105 g = open(tempname, 'w')
106 except IOError, msg:
107 f.close()
108 err(tempname+': cannot create: '+\
109 `msg`+'\n')
110 return 1
111 f.seek(0)
112 lineno = 0
113 rep(filename + ':\n')
114 continue # restart from the beginning
115 rep(`lineno` + '\n')
116 rep('< ' + line)
117 rep('> ' + newline)
118 if g is not None:
119 g.write(newline)
120
121 # End of file
122 f.close()
123 if not g: return 0 # No changes
124
125 # Finishing touch -- move files
126
127 # First copy the file's mode to the temp file
128 try:
129 statbuf = posix.stat(filename)
130 posix.chmod(tempname, statbuf[ST_MODE] & 07777)
131 except posix.error, msg:
132 err(tempname + ': warning: chmod failed (' + `msg` + ')\n')
133 # Then make a backup of the original file as filename~
134 try:
135 posix.rename(filename, filename + '~')
136 except posix.error, msg:
137 err(filename + ': warning: backup failed (' + `msg` + ')\n')
138 # Now move the temp file to the original file
139 try:
140 posix.rename(tempname, filename)
141 except posix.error, msg:
142 err(filename + ': rename failed (' + `msg` + ')\n')
143 return 1
144 # Return succes
145 return 0
146
147PAT1 = '\<\(if\|elif\|while\)\>[\0-\377]*[^<>!=]\(=\)[^=][\0-\377]*[^[]:[^]]'
148# \2 \3
149PAT2 = '\<return\>[\0-\377]*[^<>!=]\(=\)[^=]'
150# \4
151PAT = '^[ \t]*\(' + PAT1 + '\|' + PAT2 + '\)'
152# \1
153prog = regex.compile(PAT)
154
155def fixline(line):
156 while prog.match(line) >= 0:
157 regs = prog.regs
158 if regs[3] == (-1, -1):
159 a, b = regs[4]
160 else:
161 a, b = regs[3]
162 if not 0 < a < b < len(line):
163 dbg('Weird: ' + line)
164 break
165 line = line[:a] + '==' + line[b:]
166 return line
167
168main()