Statistics
| Branch: | Tag: | Revision:

mininet / util / unpep8 @ 80a8fa62

History | View | Annotate | Download (6.78 KB)

1
#!/usr/bin/python
2

    
3
"""
4
Translate from PEP8 Python style to Mininet (i.e. Arista-like) 
5
Python style
6

    
7
usage: unpep8 < old.py > new.py
8

    
9
- Reinstates CapWords for methods and instance variables
10
- Gets rid of triple single quotes
11
- Eliminates triple quotes on single lines
12
- Inserts extra spaces to improve readability
13
- Fixes Doxygen (or doxypy) ugliness
14

    
15
Does the following translations:
16

    
17
ClassName.method_name(foo = bar) -> ClassName.methodName( foo=bar )
18

    
19
Triple-single-quotes -> triple-double-quotes
20

    
21
@param foo description -> foo: description
22
@return description    -> returns: description
23
@author me             -> author: me
24
@todo(me)              -> TODO(me)
25

    
26
Bugs/Limitations:
27

    
28
- Hack to restore strings is ugly
29
- Multiline strings get mangled
30
- Comments are mangled (which is arguably the "right thing" to do, except
31
  that, for example, the left hand sides of the above would get translated!)
32
- Doesn't eliminate unnecessary backslashes
33
- Has no opinion on tab size
34
- complicated indented docstrings get flattened
35
- We don't (yet) have a filter to generate Doxygen/Doxypy
36
- Currently leaves indents on blank comment lines
37
- May lead to namespace collisions (e.g. some_thing and someThing)
38

    
39
Bob Lantz, rlantz@cs.stanford.edu
40
1/24/2010
41
"""
42

    
43
import re, sys
44

    
45
def fixUnderscoreTriplet( match ):
46
   "Translate a matched triplet of the form a_b to aB."
47
   triplet = match.group()
48
   return triplet[ :-2 ] + triplet[ -1 ].capitalize()
49

    
50
def reinstateCapWords( text ):
51
   underscoreTriplet = re.compile( r'[A-Za-z0-9]_[A-Za-z0-9]' )
52
   return underscoreTriplet.sub( fixUnderscoreTriplet, text )
53
 
54
def replaceTripleApostrophes( text ):
55
   "Replace triple apostrophes with triple quotes."
56
   return text.replace( "'''", '"""')
57

    
58
def simplifyTripleQuotes( text ):
59
   "Fix single-line doc strings."
60
   r = re.compile( r'"""([^\"\n]+)"""' )
61
   return r.sub( r'"\1"', text )
62
   
63
def insertExtraSpaces( text ):
64
   "Insert extra spaces inside of parentheses and brackets/curly braces."
65
   lparen = re.compile( r'\((?![\s\)])' )
66
   text = lparen.sub( r'( ', text )
67
   rparen = re.compile( r'([^\s\(])(?=\))' )
68
   text = rparen.sub( r'\1 ', text)
69
   # brackets
70
   lbrack = re.compile( r'\[(?![\s\]])' )
71
   text = lbrack.sub( r'[ ', text )
72
   rbrack = re.compile( r'([^\s\[])(?=\])' )
73
   text = rbrack.sub( r'\1 ', text)
74
   # curly braces
75
   lcurly = re.compile( r'\{(?![\s\}])' )
76
   text = lcurly.sub( r'{ ', text )
77
   rcurly = re.compile( r'([^\s\{])(?=\})' )
78
   text = rcurly.sub( r'\1 ', text)   
79
   return text
80
   
81
def fixDoxygen( text ):
82
   """Translate @param foo to foo:, @return bar to returns: bar, and
83
      @author me to author: me"""
84
   param = re.compile( r'@param (\w+)' )
85
   text = param.sub( r'\1:', text )
86
   returns = re.compile( r'@return' )
87
   text = returns.sub( r'returns:', text )
88
   author = re.compile( r'@author' )
89
   text = author.sub( r'author:', text)
90
   # @todo -> TODO
91
   text = text.replace( '@todo', 'TODO' )
92
   return text
93

    
94
def removeCommentFirstBlankLine( text ):
95
   "Remove annoying blank lines after first line in comments."
96
   line = re.compile( r'("""[^\n]*\n)\s*\n', re.MULTILINE )
97
   return line.sub( r'\1', text )
98
   
99
def fixArgs( match, kwarg = re.compile( r'(\w+) = ' ) ):
100
   "Replace foo = bar with foo=bar."
101
   return kwarg.sub( r'\1=', match.group() )
102
   
103
def fixKeywords( text ):
104
   "Change keyword argumentsfrom foo = bar to foo=bar."
105
   args = re.compile( r'\(([^\)]+)\)', re.MULTILINE )
106
   return args.sub( fixArgs, text )
107

    
108
# Unfortunately, Python doesn't natively support balanced or recursive
109
# regular expressions. We could use PyParsing, but that opens another can
110
# of worms. For now, we just have a cheap hack to restore strings,
111
# so we don't end up accidentally mangling things like messages, search strings,
112
# and regular expressions.
113

    
114
def lineIter( text ):
115
   "Simple iterator over lines in text."
116
   for line in text.splitlines(): yield line
117
   
118
def stringIter( strList ):
119
   "Yield strings in strList."
120
   for s in strList: yield s
121

    
122
def restoreRegex( regex, old, new ):
123
   "Find regexes in old and restore them into new."
124
   oldStrs = regex.findall( old )
125
   # Sanity check - count should be the same!
126
   newStrs = regex.findall( new )
127
   assert len( oldStrs ) == len( newStrs )
128
   # Replace newStrs with oldStrs
129
   siter = stringIter( oldStrs )
130
   reps = lambda dummy: siter.next()
131
   return regex.sub( reps, new )
132

    
133
# This is a cheap hack, and it may not work 100%, since
134
# it doesn't handle multiline strings.
135
# However, it should be mostly harmless...
136
   
137
def restoreStrings( oldText, newText ):
138
   "Restore strings from oldText into newText, returning result."
139
   oldLines, newLines = lineIter( oldText ), lineIter( newText )
140
   quoteStrings = re.compile( r'("[^"]*")' )
141
   tickStrings = re.compile( r"('[^']*')" )
142
   result = ''
143
   # It would be nice if we could blast the whole file, but for
144
   # now it seems to work line-by-line
145
   for newLine in newLines:
146
      oldLine = oldLines.next()
147
      newLine = restoreRegex( quoteStrings, oldLine, newLine )
148
      newLine = restoreRegex( tickStrings, oldLine, newLine )
149
      result += newLine + '\n'
150
   return result
151
   
152
# This might be slightly controversial, since it uses
153
# three spaces to line up multiline comments. However,
154
# I much prefer it. Limitations: if you have deeper
155
# indents in comments, they will be eliminated. ;-(
156

    
157
def fixComment( match, 
158
   indentExp=re.compile( r'\n([ ]*)(?=[^/s])', re.MULTILINE ),
159
   trailingQuotes=re.compile( r'\s+"""' ) ):
160
   "Re-indent comment, and join trailing quotes."
161
   originalIndent = match.group( 1 )
162
   comment = match.group( 2 )
163
   indent = '\n' + originalIndent
164
   # Exception: leave unindented things unindented!
165
   if len( originalIndent ) is not 0: indent += '   '
166
   comment = indentExp.sub( indent, comment )
167
   return originalIndent + trailingQuotes.sub( '"""', comment )
168
   
169
def fixCommentIndents( text ):
170
   "Fix multiline comment indentation."
171
   comments = re.compile( r'^([ ]*)("""[^"]*""")$', re.MULTILINE )
172
   return comments.sub( fixComment, text )
173
 
174
def removeBogusLinefeeds( text ):
175
   "Remove extra linefeeds at the end of single-line comments."
176
   bogusLfs = re.compile( r'"([^"\n]*)\n"', re.MULTILINE )
177
   return bogusLfs.sub( '"\1"', text)
178
   
179
def convertFromPep8( program ):
180
   oldProgram = program
181
   # Program text transforms
182
   program = reinstateCapWords( program )
183
   program = fixKeywords( program )
184
   program = insertExtraSpaces( program )
185
   # Undo string damage
186
   program = restoreStrings( oldProgram, program )
187
   # Docstring transforms
188
   program = replaceTripleApostrophes( program )
189
   program = simplifyTripleQuotes( program )
190
   program = fixDoxygen( program )
191
   program = fixCommentIndents( program )
192
   program = removeBogusLinefeeds( program )
193
   # Destructive transforms (these can delete lines)
194
   program = removeCommentFirstBlankLine( program )
195
   return program
196

    
197
if __name__ == '__main__':
198
   print convertFromPep8( sys.stdin.read() )