3 # Change the #! line occurring in Python scripts. The new interpreter
4 # pathname must be given with a -i option.
6 # Command line arguments are files or directories to be processed.
7 # Directories are searched recursively for files whose name looks
8 # like a python module.
9 # Symbolic links are always ignored (except as explicit directory
10 # arguments). Of course, the original file is kept as a back-up
11 # (with a "~" attached to its name).
13 # Undoubtedly you can do this using find and sed or perl, but this is
14 # a nice example of Python code that recurses down a directory tree
15 # and uses regular expressions. Also note several subtleties like
16 # preserving the file's mode and avoiding to even write a temp file
17 # when no changes are needed for a file.
19 # NB: by changing only the function fixfile() you can turn this
20 # into a program for a different change to Python programs...
28 err
= sys
.stderr
.write
30 rep
= sys
.stdout
.write
32 new_interpreter
= None
35 global new_interpreter
36 usage
= ('usage: %s -i /interpreter file-or-directory ...\n' %
39 opts
, args
= getopt
.getopt(sys
.argv
[1:], 'i:')
40 except getopt
.error
, msg
:
47 if not new_interpreter
or new_interpreter
[0] != '/' or not args
:
48 err('-i option or file-or-directory missing\n')
53 if os
.path
.isdir(arg
):
54 if recursedown(arg
): bad
= 1
55 elif os
.path
.islink(arg
):
56 err(arg
+ ': will not process symbolic links\n')
62 ispythonprog
= re
.compile('^[a-zA-Z0-9_]+\.py$')
64 return ispythonprog
.match(name
) >= 0
66 def recursedown(dirname
):
67 dbg('recursedown(%r)\n' % (dirname
,))
70 names
= os
.listdir(dirname
)
72 err('%s: cannot list directory: %r\n' % (dirname
, msg
))
77 if name
in (os
.curdir
, os
.pardir
): continue
78 fullname
= os
.path
.join(dirname
, name
)
79 if os
.path
.islink(fullname
): pass
80 elif os
.path
.isdir(fullname
):
81 subdirs
.append(fullname
)
83 if fix(fullname
): bad
= 1
84 for fullname
in subdirs
:
85 if recursedown(fullname
): bad
= 1
89 ## dbg('fix(%r)\n' % (filename,))
91 f
= open(filename
, 'r')
93 err('%s: cannot open: %r\n' % (filename
, msg
))
98 rep(filename
+': no change\n')
101 head
, tail
= os
.path
.split(filename
)
102 tempname
= os
.path
.join(head
, '@' + tail
)
104 g
= open(tempname
, 'w')
107 err('%s: cannot create: %r\n' % (tempname
, msg
))
109 rep(filename
+ ': updating\n')
113 buf
= f
.read(BUFSIZE
)
119 # Finishing touch -- move files
121 # First copy the file's mode to the temp file
123 statbuf
= os
.stat(filename
)
124 os
.chmod(tempname
, statbuf
[ST_MODE
] & 07777)
125 except os
.error
, msg
:
126 err('%s: warning: chmod failed (%r)\n' % (tempname
, msg
))
127 # Then make a backup of the original file as filename~
129 os
.rename(filename
, filename
+ '~')
130 except os
.error
, msg
:
131 err('%s: warning: backup failed (%r)\n' % (filename
, msg
))
132 # Now move the temp file to the original file
134 os
.rename(tempname
, filename
)
135 except os
.error
, msg
:
136 err('%s: rename failed (%r)\n' % (filename
, msg
))
142 if not line
.startswith('#!'):
144 if "python" not in line
:
146 return '#! %s\n' % new_interpreter
148 if __name__
== '__main__':