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...
29 err
= sys
.stderr
.write
31 rep
= sys
.stdout
.write
33 new_interpreter
= None
36 global new_interpreter
37 usage
= ('usage: %s -i /interpreter file-or-directory ...\n' %
40 opts
, args
= getopt
.getopt(sys
.argv
[1:], 'i:')
41 except getopt
.error
, msg
:
48 if not new_interpreter
or new_interpreter
[0] != '/' or not args
:
49 err('-i option or file-or-directory missing\n')
54 if os
.path
.isdir(arg
):
55 if recursedown(arg
): bad
= 1
56 elif os
.path
.islink(arg
):
57 err(arg
+ ': will not process symbolic links\n')
63 ispythonprog
= regex
.compile('^[a-zA-Z0-9_]+\.py$')
65 return ispythonprog
.match(name
) >= 0
67 def recursedown(dirname
):
68 dbg('recursedown(' + `dirname`
+ ')\n')
71 names
= os
.listdir(dirname
)
73 err(dirname
+ ': cannot list directory: ' + `msg`
+ '\n')
78 if name
in (os
.curdir
, os
.pardir
): continue
79 fullname
= os
.path
.join(dirname
, name
)
80 if os
.path
.islink(fullname
): pass
81 elif os
.path
.isdir(fullname
):
82 subdirs
.append(fullname
)
84 if fix(fullname
): bad
= 1
85 for fullname
in subdirs
:
86 if recursedown(fullname
): bad
= 1
90 ## dbg('fix(' + `filename` + ')\n')
92 f
= open(filename
, 'r')
94 err(filename
+ ': cannot open: ' + `msg`
+ '\n')
99 rep(filename
+': no change\n')
102 head
, tail
= os
.path
.split(filename
)
103 tempname
= os
.path
.join(head
, '@' + tail
)
105 g
= open(tempname
, 'w')
108 err(tempname
+': cannot create: '+`msg`
+'\n')
110 rep(filename
+ ': updating\n')
114 buf
= f
.read(BUFSIZE
)
120 # Finishing touch -- move files
122 # First copy the file's mode to the temp file
124 statbuf
= os
.stat(filename
)
125 os
.chmod(tempname
, statbuf
[ST_MODE
] & 07777)
126 except os
.error
, msg
:
127 err(tempname
+ ': warning: chmod failed (' + `msg`
+ ')\n')
128 # Then make a backup of the original file as filename~
130 os
.rename(filename
, filename
+ '~')
131 except os
.error
, msg
:
132 err(filename
+ ': warning: backup failed (' + `msg`
+ ')\n')
133 # Now move the temp file to the original file
135 os
.rename(tempname
, filename
)
136 except os
.error
, msg
:
137 err(filename
+ ': rename failed (' + `msg`
+ ')\n')
145 if string
.find(line
, "python") < 0:
147 return '#! %s\n' % new_interpreter