]>
Commit | Line | Data |
---|---|---|
4710c53d | 1 | #! /usr/bin/env python\r |
2 | \r | |
3 | # Change the #! line occurring in Python scripts. The new interpreter\r | |
4 | # pathname must be given with a -i option.\r | |
5 | #\r | |
6 | # Command line arguments are files or directories to be processed.\r | |
7 | # Directories are searched recursively for files whose name looks\r | |
8 | # like a python module.\r | |
9 | # Symbolic links are always ignored (except as explicit directory\r | |
10 | # arguments). Of course, the original file is kept as a back-up\r | |
11 | # (with a "~" attached to its name).\r | |
12 | #\r | |
13 | # Undoubtedly you can do this using find and sed or perl, but this is\r | |
14 | # a nice example of Python code that recurses down a directory tree\r | |
15 | # and uses regular expressions. Also note several subtleties like\r | |
16 | # preserving the file's mode and avoiding to even write a temp file\r | |
17 | # when no changes are needed for a file.\r | |
18 | #\r | |
19 | # NB: by changing only the function fixfile() you can turn this\r | |
20 | # into a program for a different change to Python programs...\r | |
21 | \r | |
22 | import sys\r | |
23 | import re\r | |
24 | import os\r | |
25 | from stat import *\r | |
26 | import getopt\r | |
27 | \r | |
28 | err = sys.stderr.write\r | |
29 | dbg = err\r | |
30 | rep = sys.stdout.write\r | |
31 | \r | |
32 | new_interpreter = None\r | |
33 | \r | |
34 | def main():\r | |
35 | global new_interpreter\r | |
36 | usage = ('usage: %s -i /interpreter file-or-directory ...\n' %\r | |
37 | sys.argv[0])\r | |
38 | try:\r | |
39 | opts, args = getopt.getopt(sys.argv[1:], 'i:')\r | |
40 | except getopt.error, msg:\r | |
41 | err(msg + '\n')\r | |
42 | err(usage)\r | |
43 | sys.exit(2)\r | |
44 | for o, a in opts:\r | |
45 | if o == '-i':\r | |
46 | new_interpreter = a\r | |
47 | if not new_interpreter or new_interpreter[0] != '/' or not args:\r | |
48 | err('-i option or file-or-directory missing\n')\r | |
49 | err(usage)\r | |
50 | sys.exit(2)\r | |
51 | bad = 0\r | |
52 | for arg in args:\r | |
53 | if os.path.isdir(arg):\r | |
54 | if recursedown(arg): bad = 1\r | |
55 | elif os.path.islink(arg):\r | |
56 | err(arg + ': will not process symbolic links\n')\r | |
57 | bad = 1\r | |
58 | else:\r | |
59 | if fix(arg): bad = 1\r | |
60 | sys.exit(bad)\r | |
61 | \r | |
62 | ispythonprog = re.compile('^[a-zA-Z0-9_]+\.py$')\r | |
63 | def ispython(name):\r | |
64 | return ispythonprog.match(name) >= 0\r | |
65 | \r | |
66 | def recursedown(dirname):\r | |
67 | dbg('recursedown(%r)\n' % (dirname,))\r | |
68 | bad = 0\r | |
69 | try:\r | |
70 | names = os.listdir(dirname)\r | |
71 | except os.error, msg:\r | |
72 | err('%s: cannot list directory: %r\n' % (dirname, msg))\r | |
73 | return 1\r | |
74 | names.sort()\r | |
75 | subdirs = []\r | |
76 | for name in names:\r | |
77 | if name in (os.curdir, os.pardir): continue\r | |
78 | fullname = os.path.join(dirname, name)\r | |
79 | if os.path.islink(fullname): pass\r | |
80 | elif os.path.isdir(fullname):\r | |
81 | subdirs.append(fullname)\r | |
82 | elif ispython(name):\r | |
83 | if fix(fullname): bad = 1\r | |
84 | for fullname in subdirs:\r | |
85 | if recursedown(fullname): bad = 1\r | |
86 | return bad\r | |
87 | \r | |
88 | def fix(filename):\r | |
89 | ## dbg('fix(%r)\n' % (filename,))\r | |
90 | try:\r | |
91 | f = open(filename, 'r')\r | |
92 | except IOError, msg:\r | |
93 | err('%s: cannot open: %r\n' % (filename, msg))\r | |
94 | return 1\r | |
95 | line = f.readline()\r | |
96 | fixed = fixline(line)\r | |
97 | if line == fixed:\r | |
98 | rep(filename+': no change\n')\r | |
99 | f.close()\r | |
100 | return\r | |
101 | head, tail = os.path.split(filename)\r | |
102 | tempname = os.path.join(head, '@' + tail)\r | |
103 | try:\r | |
104 | g = open(tempname, 'w')\r | |
105 | except IOError, msg:\r | |
106 | f.close()\r | |
107 | err('%s: cannot create: %r\n' % (tempname, msg))\r | |
108 | return 1\r | |
109 | rep(filename + ': updating\n')\r | |
110 | g.write(fixed)\r | |
111 | BUFSIZE = 8*1024\r | |
112 | while 1:\r | |
113 | buf = f.read(BUFSIZE)\r | |
114 | if not buf: break\r | |
115 | g.write(buf)\r | |
116 | g.close()\r | |
117 | f.close()\r | |
118 | \r | |
119 | # Finishing touch -- move files\r | |
120 | \r | |
121 | # First copy the file's mode to the temp file\r | |
122 | try:\r | |
123 | statbuf = os.stat(filename)\r | |
124 | os.chmod(tempname, statbuf[ST_MODE] & 07777)\r | |
125 | except os.error, msg:\r | |
126 | err('%s: warning: chmod failed (%r)\n' % (tempname, msg))\r | |
127 | # Then make a backup of the original file as filename~\r | |
128 | try:\r | |
129 | os.rename(filename, filename + '~')\r | |
130 | except os.error, msg:\r | |
131 | err('%s: warning: backup failed (%r)\n' % (filename, msg))\r | |
132 | # Now move the temp file to the original file\r | |
133 | try:\r | |
134 | os.rename(tempname, filename)\r | |
135 | except os.error, msg:\r | |
136 | err('%s: rename failed (%r)\n' % (filename, msg))\r | |
137 | return 1\r | |
138 | # Return succes\r | |
139 | return 0\r | |
140 | \r | |
141 | def fixline(line):\r | |
142 | if not line.startswith('#!'):\r | |
143 | return line\r | |
144 | if "python" not in line:\r | |
145 | return line\r | |
146 | return '#! %s\n' % new_interpreter\r | |
147 | \r | |
148 | if __name__ == '__main__':\r | |
149 | main()\r |