]>
git.ipfire.org Git - ipfire-3.x.git/blob - tools/code-beautify
3 """code-saner [-d][-r][-v] [ path ... ]
5 -d (--dryrun) Dry run. Analyze, but don't make any changes to, files.
6 -r (--recurse) Recurse. Search for all .py files in subdirectories too.
7 -b (--backup) Keep backups of files.
8 -v (--verbose) Verbose. Print informative msgs; else no output.
9 -h (--help) Help. Print this usage information and exit.
25 print >> sys
.stderr
, msg
26 print >> sys
.stderr
, __doc__
31 sys
.stderr
.write(sep
+ str(arg
))
33 sys
.stderr
.write("\n")
37 global verbose
, recurse
, dryrun
, backup
39 opts
, args
= getopt
.getopt(sys
.argv
[1:], "drbvh",
40 ["dryrun", "recurse", "backup" "verbose", "help"])
41 except getopt
.error
, msg
:
45 if o
in ('-d', '--dryrun'):
47 elif o
in ('-r', '--recurse'):
49 elif o
in ('-b', '--backup'):
51 elif o
in ('-v', '--verbose'):
53 elif o
in ('-h', '--help'):
57 r
= Reindenter(sys
.stdin
)
65 'images' : (".png", ".gif", ".jpg", ".jpeg",),
67 'config' : (".conf", ".cnf", ".cf", ".cfg", ".config",),
69 'script' : (".pl", ".c", ".h", ".sh", ".txt",),
77 errprint("%s: I/O Error: %s" % (file, str(msg
)))
81 for ending
in endings
['python']:
82 if file.endswith(ending
):
83 object = PythonFile(f
=f
, file=file)
86 for ending
in endings
['config']:
87 if file.endswith(ending
):
88 object = ConfigFile(f
=f
, file=file)
91 for ending
in endings
['script']:
92 if file.endswith(ending
):
93 object = ScriptFile(f
=f
, file=file)
96 for ending
in endings
['styles']:
97 if file.endswith(ending
):
98 object = StyleFile(f
=f
, file=file)
101 for ending
in endings
['images']:
102 if file.endswith(ending
):
103 object = ImageFile(f
=f
, file=file)
106 return object or UnknownFile(f
=f
, file=file)
109 if os
.path
.isdir(file) and not os
.path
.islink(file):
111 print "listing directory", file
112 names
= os
.listdir(file)
114 fullname
= os
.path
.join(file, name
)
115 if ((recurse
and not os
.path
.islink(fullname
))):
120 print "checking", file, "...",
130 print "But this is a dry run, so leaving it alone."
134 if os
.path
.exists(bak
):
138 print "renamed", file, "to", bak
143 print "wrote new", file
148 def _rstrip(line
, junk
='\n \t'):
149 """Return line stripped of trailing spaces, tabs, newlines.
151 Note that line.rstrip() instead also strips sundry control characters,
152 but at least one known Emacs user expects to keep junk like that, not
153 mentioning Barry by name or anything <wink>.
157 while i
> 0 and line
[i
-1] in junk
:
161 # Count number of leading blanks.
164 while i
< n
and line
[i
] == " ":
169 def __init__(self
, f
, file=None):
175 # errprint("Can't guess filetype of given file %s" % self.file)
177 def rm_trailing_lines(self
, lines
):
178 # Remove trailing empty lines.
179 while lines
and lines
[-1] == "\n":
183 def expand_tabs(self
, lines
):
184 return [_rstrip(line
).expandtabs() + "\n" for line
in lines
]
186 class TextFile(DefaultFile
):
187 def __init__(self
, f
, file=None):
188 self
.file = file # Save filename.
189 self
.raw
= f
.readlines() # Raw file lines.
191 # File lines, rstripped. Dummy at start is so
192 # that we can use tokenize's 1-based line numbering easily.
193 # Note that a line is all-blank iff it's "\n".
194 self
.lines
= [_rstrip(line
) + "\n" for line
in self
.raw
]
197 self
.after
= self
.lines
198 return self
.raw
!= self
.after
201 f
.writelines(self
.after
)
203 class PythonFile(TextFile
):
204 def __init__(self
, f
, file=None):
205 TextFile
.__init
__(self
, f
, file)
206 self
.find_stmt
= 1 # next token begins a fresh stmt?
207 self
.level
= 0 # current indent level
209 self
.lines
= self
.expand_tabs(self
.lines
)
210 self
.lines
.insert(0, None)
211 self
.index
= 1 # index into self.lines of next line
213 # List of (lineno, indentlevel) pairs, one for each stmt and
214 # comment line. indentlevel is -1 for comment lines, as a
215 # signal that tokenize doesn't know what to do about them;
216 # indeed, they're our headache!
220 tokenize
.tokenize(self
.getline
, self
.tokeneater
)
221 # Remove trailing empty lines.
222 lines
= self
.rm_trailing_lines(self
.lines
)
225 stats
.append((len(lines
), 0))
226 # Map count of leading spaces to # we want.
228 # Program after transformation.
229 after
= self
.after
= []
230 # Copy over initial empty lines -- there's nothing to do until
231 # we see a line with *something* on it.
233 after
.extend(lines
[1:i
])
234 for i
in range(len(stats
)-1):
235 thisstmt
, thislevel
= stats
[i
]
236 nextstmt
= stats
[i
+1][0]
237 have
= getlspace(lines
[thisstmt
])
242 # An indented comment line. If we saw the same
243 # indentation before, reuse what it most recently
245 want
= have2want
.get(have
, -1)
247 # Then it probably belongs to the next real stmt.
248 for j
in xrange(i
+1, len(stats
)-1):
249 jline
, jlevel
= stats
[j
]
251 if have
== getlspace(lines
[jline
]):
254 if want
< 0: # Maybe it's a hanging
255 # comment like this one,
256 # in which case we should shift it like its base
258 for j
in xrange(i
-1, -1, -1):
259 jline
, jlevel
= stats
[j
]
261 want
= have
+ getlspace(after
[jline
-1]) - \
262 getlspace(lines
[jline
])
265 # Still no luck -- leave it alone.
270 have2want
[have
] = want
272 if diff
== 0 or have
== 0:
273 after
.extend(lines
[thisstmt
:nextstmt
])
275 for line
in lines
[thisstmt
:nextstmt
]:
280 after
.append(" " * diff
+ line
)
282 remove
= min(getlspace(line
), -diff
)
283 after
.append(line
[remove
:])
284 return self
.raw
!= self
.after
286 # Line-getter for tokenize.
288 if self
.index
>= len(self
.lines
):
291 line
= self
.lines
[self
.index
]
295 # Line-eater for tokenize.
296 def tokeneater(self
, type, token
, (sline
, scol
), end
, line
,
297 INDENT
=tokenize
.INDENT
,
298 DEDENT
=tokenize
.DEDENT
,
299 NEWLINE
=tokenize
.NEWLINE
,
300 COMMENT
=tokenize
.COMMENT
,
304 # A program statement, or ENDMARKER, will eventually follow,
305 # after some (possibly empty) run of tokens of the form
306 # (NL | COMMENT)* (INDENT | DEDENT+)?
317 elif type == COMMENT
:
319 self
.stats
.append((sline
, -1))
320 # but we're still looking for a new stmt, so leave
327 # This is the first "real token" following a NEWLINE, so it
328 # must be the first token of the next program statement, or an
331 if line
: # not endmarker
332 self
.stats
.append((sline
, self
.level
))
334 UnknownFile
= ImageFile
= DefaultFile
# just the empty file object, do nothing
335 ConfigFile
= ScriptFile
= StyleFile
= TextFile
# simple textfiles
337 if __name__
== '__main__':