rev |
line source |
ewan@6766
|
1 #!/usr/bin/env python
|
ewan@6766
|
2
|
ewan@6766
|
3 # pythfilter.py v1.5.5, written by Matthias Baas (baas@ira.uka.de)
|
ewan@6766
|
4
|
ewan@6766
|
5 # Doxygen filter which can be used to document Python source code.
|
ewan@6766
|
6 # Classes (incl. methods) and functions can be documented.
|
ewan@6766
|
7 # Every comment that begins with ## is literally turned into an
|
ewan@6766
|
8 # Doxygen comment. Consecutive comment lines are turned into
|
ewan@6766
|
9 # comment blocks (-> /** ... */).
|
ewan@6766
|
10 # All the stuff is put inside a namespace with the same name as
|
ewan@6766
|
11 # the source file.
|
ewan@6766
|
12
|
ewan@6766
|
13 # Conversions:
|
ewan@6766
|
14 # ============
|
ewan@6766
|
15 # ##-blocks -> /** ... */
|
ewan@6766
|
16 # "class name(base): ..." -> "class name : public base {...}"
|
ewan@6766
|
17 # "def name(params): ..." -> "name(params) {...}"
|
ewan@6766
|
18
|
ewan@6766
|
19 # Changelog:
|
ewan@6766
|
20 # 21.01.2003: Raw (r"") or unicode (u"") doc string will now be properly
|
ewan@6766
|
21 # handled. (thanks to Richard Laager for the patch)
|
ewan@6766
|
22 # 22.12.2003: Fixed a bug where no function names would be output for "def"
|
ewan@6766
|
23 # blocks that were not in a class.
|
ewan@6766
|
24 # (thanks to Richard Laager for the patch)
|
ewan@6766
|
25 # 12.12.2003: Implemented code to handle static and class methods with
|
ewan@6766
|
26 # this logic: Methods with "self" as the first argument are
|
ewan@6766
|
27 # non-static. Methods with "cls" are Python class methods,
|
ewan@6766
|
28 # which translate into static methods for Doxygen. Other
|
ewan@6766
|
29 # methods are assumed to be static methods. As should be
|
ewan@6766
|
30 # obvious, this logic doesn't take into account if the method
|
ewan@6766
|
31 # is actually setup as a classmethod() or a staticmethod(),
|
ewan@6766
|
32 # just if it follows the normal conventions.
|
ewan@6766
|
33 # (thanks to Richard Laager for the patch)
|
ewan@6766
|
34 # 11.12.2003: Corrected #includes to use os.path.sep instead of ".". Corrected
|
ewan@6766
|
35 # namespace code to use "::" instead of ".".
|
ewan@6766
|
36 # (thanks to Richard Laager for the patch)
|
ewan@6766
|
37 # 11.12.2003: Methods beginning with two underscores that end with
|
ewan@6766
|
38 # something other than two underscores are considered private
|
ewan@6766
|
39 # and are handled accordingly.
|
ewan@6766
|
40 # (thanks to Richard Laager for the patch)
|
ewan@6766
|
41 # 03.12.2003: The first parameter of class methods (self) is removed from
|
ewan@6766
|
42 # the documentation.
|
ewan@6766
|
43 # 03.11.2003: The module docstring will be used as namespace documentation
|
ewan@6766
|
44 # (thanks to Joe Bronkema for the patch)
|
ewan@6766
|
45 # 08.07.2003: Namespaces get a default documentation so that the namespace
|
ewan@6766
|
46 # and its contents will show up in the generated documentation.
|
ewan@6766
|
47 # 05.02.2003: Directories will be delted during synchronization.
|
ewan@6766
|
48 # 31.01.2003: -f option & filtering entire directory trees.
|
ewan@6766
|
49 # 10.08.2002: In base classes the '.' will be replaced by '::'
|
ewan@6766
|
50 # 18.07.2002: * and ** will be translated into arguments
|
ewan@6766
|
51 # 18.07.2002: Argument lists may contain default values using constructors.
|
ewan@6766
|
52 # 18.06.2002: Support for ## public:
|
ewan@6766
|
53 # 21.01.2002: from ... import will be translated to "using namespace ...;"
|
ewan@6766
|
54 # TODO: "from ... import *" vs "from ... import names"
|
ewan@6766
|
55 # TODO: Using normal imports: name.name -> name::name
|
ewan@6766
|
56 # 20.01.2002: #includes will be placed in front of the namespace
|
ewan@6766
|
57
|
ewan@6766
|
58 ######################################################################
|
ewan@6766
|
59
|
ewan@6766
|
60 # The program is written as a state machine with the following states:
|
ewan@6766
|
61 #
|
ewan@6766
|
62 # - OUTSIDE The current position is outside any comment,
|
ewan@6766
|
63 # class definition or function.
|
ewan@6766
|
64 #
|
ewan@6766
|
65 # - BUILD_COMMENT Begins with first "##".
|
ewan@6766
|
66 # Ends with the first token that is no "##"
|
ewan@6766
|
67 # at the same column as before.
|
ewan@6766
|
68 #
|
ewan@6766
|
69 # - BUILD_CLASS_DECL Begins with "class".
|
ewan@6766
|
70 # Ends with ":"
|
ewan@6766
|
71 # - BUILD_CLASS_BODY Begins just after BUILD_CLASS_DECL.
|
ewan@6766
|
72 # The first following token (which is no comment)
|
ewan@6766
|
73 # determines indentation depth.
|
ewan@6766
|
74 # Ends with a token that has a smaller indendation.
|
ewan@6766
|
75 #
|
ewan@6766
|
76 # - BUILD_DEF_DECL Begins with "def".
|
ewan@6766
|
77 # Ends with ":".
|
ewan@6766
|
78 # - BUILD_DEF_BODY Begins just after BUILD_DEF_DECL.
|
ewan@6766
|
79 # The first following token (which is no comment)
|
ewan@6766
|
80 # determines indentation depth.
|
ewan@6766
|
81 # Ends with a token that has a smaller indendation.
|
ewan@6766
|
82
|
ewan@6766
|
83 import getopt
|
ewan@6766
|
84 import glob
|
ewan@6766
|
85 import os.path
|
ewan@6766
|
86 import re
|
ewan@6766
|
87 import shutil
|
ewan@6766
|
88 import string
|
ewan@6766
|
89 import sys
|
ewan@6766
|
90 import token
|
ewan@6766
|
91 import tokenize
|
ewan@6766
|
92
|
ewan@6766
|
93 from stat import *
|
ewan@6766
|
94
|
ewan@6766
|
95 OUTSIDE = 0
|
ewan@6766
|
96 BUILD_COMMENT = 1
|
ewan@6766
|
97 BUILD_CLASS_DECL = 2
|
ewan@6766
|
98 BUILD_CLASS_BODY = 3
|
ewan@6766
|
99 BUILD_DEF_DECL = 4
|
ewan@6766
|
100 BUILD_DEF_BODY = 5
|
ewan@6766
|
101 IMPORT = 6
|
ewan@6766
|
102 IMPORT_OP = 7
|
ewan@6766
|
103 IMPORT_APPEND = 8
|
ewan@6766
|
104
|
ewan@6766
|
105 # Output file stream
|
ewan@6766
|
106 outfile = sys.stdout
|
ewan@6766
|
107
|
ewan@6766
|
108 # Output buffer
|
ewan@6766
|
109 outbuffer = []
|
ewan@6766
|
110
|
ewan@6766
|
111 out_row = 1
|
ewan@6766
|
112 out_col = 0
|
ewan@6766
|
113
|
ewan@6766
|
114 # Variables used by rec_name_n_param()
|
ewan@6766
|
115 name = ""
|
ewan@6766
|
116 param = ""
|
ewan@6766
|
117 doc_string = ""
|
ewan@6766
|
118 record_state = 0
|
ewan@6766
|
119 bracket_counter = 0
|
ewan@6766
|
120
|
ewan@6766
|
121 # Tuple: (row,column)
|
ewan@6766
|
122 class_spos = (0,0)
|
ewan@6766
|
123 def_spos = (0,0)
|
ewan@6766
|
124 import_spos = (0,0)
|
ewan@6766
|
125
|
ewan@6766
|
126 # Which import was used? ("import" or "from")
|
ewan@6766
|
127 import_token = ""
|
ewan@6766
|
128
|
ewan@6766
|
129 # Comment block buffer
|
ewan@6766
|
130 comment_block = []
|
ewan@6766
|
131 comment_finished = 0
|
ewan@6766
|
132
|
ewan@6766
|
133 # Imported modules
|
ewan@6766
|
134 modules = []
|
ewan@6766
|
135
|
ewan@6766
|
136 # Program state
|
ewan@6766
|
137 stateStack = [OUTSIDE]
|
ewan@6766
|
138
|
ewan@6766
|
139 # Keep track of whether module has a docstring
|
ewan@6766
|
140 module_has_docstring = False
|
ewan@6766
|
141
|
ewan@6766
|
142 # Keep track of member protection
|
ewan@6766
|
143 protection_level = "public"
|
ewan@6766
|
144 private_member = False
|
ewan@6766
|
145
|
ewan@6766
|
146 # Keep track of the module namespace
|
ewan@6766
|
147 namespace = ""
|
ewan@6766
|
148
|
ewan@6766
|
149 ######################################################################
|
ewan@6766
|
150 # Output string s. '\n' may only be at the end of the string (not
|
ewan@6766
|
151 # somewhere in the middle).
|
ewan@6766
|
152 #
|
ewan@6766
|
153 # In: s - String
|
ewan@6766
|
154 # spos - Startpos
|
ewan@6766
|
155 ######################################################################
|
ewan@6766
|
156 def output(s,spos, immediate=0):
|
ewan@6766
|
157 global outbuffer, out_row, out_col, outfile
|
ewan@6766
|
158
|
ewan@6766
|
159 os = string.rjust(s,spos[1]-out_col+len(s))
|
ewan@6766
|
160
|
ewan@6766
|
161 if immediate:
|
ewan@6766
|
162 outfile.write(os)
|
ewan@6766
|
163 else:
|
ewan@6766
|
164 outbuffer.append(os)
|
ewan@6766
|
165
|
ewan@6766
|
166 assert -1 == string.find(s[0:-2], "\n"), s
|
ewan@6766
|
167
|
ewan@6766
|
168 if (s[-1:]=="\n"):
|
ewan@6766
|
169 out_row = out_row+1
|
ewan@6766
|
170 out_col = 0
|
ewan@6766
|
171 else:
|
ewan@6766
|
172 out_col = spos[1]+len(s)
|
ewan@6766
|
173
|
ewan@6766
|
174
|
ewan@6766
|
175 ######################################################################
|
ewan@6766
|
176 # Records a name and parameters. The name is either a class name or
|
ewan@6766
|
177 # a function name. Then the parameter is either the base class or
|
ewan@6766
|
178 # the function parameters.
|
ewan@6766
|
179 # The name is stored in the global variable "name", the parameters
|
ewan@6766
|
180 # in "param".
|
ewan@6766
|
181 # The variable "record_state" holds the current state of this internal
|
ewan@6766
|
182 # state machine.
|
ewan@6766
|
183 # The recording is started by calling start_recording().
|
ewan@6766
|
184 #
|
ewan@6766
|
185 # In: type, tok
|
ewan@6766
|
186 ######################################################################
|
ewan@6766
|
187 def rec_name_n_param(type, tok):
|
ewan@6766
|
188 global record_state,name,param,doc_string,bracket_counter
|
ewan@6766
|
189 s = record_state
|
ewan@6766
|
190 # State 0: Do nothing.
|
ewan@6766
|
191 if (s==0):
|
ewan@6766
|
192 return
|
ewan@6766
|
193 # State 1: Remember name.
|
ewan@6766
|
194 elif (s==1):
|
ewan@6766
|
195 name = tok
|
ewan@6766
|
196 record_state = 2
|
ewan@6766
|
197 # State 2: Wait for opening bracket or colon
|
ewan@6766
|
198 elif (s==2):
|
ewan@6766
|
199 if (tok=='('):
|
ewan@6766
|
200 bracket_counter = 1
|
ewan@6766
|
201 record_state=3
|
ewan@6766
|
202 if (tok==':'): record_state=4
|
ewan@6766
|
203 # State 3: Store parameter (or base class) and wait for an ending bracket
|
ewan@6766
|
204 elif (s==3):
|
ewan@6766
|
205 if (tok=='*' or tok=='**'):
|
ewan@6766
|
206 tok=''
|
ewan@6766
|
207 if (tok=='('):
|
ewan@6766
|
208 bracket_counter = bracket_counter+1
|
ewan@6766
|
209 if (tok==')'):
|
ewan@6766
|
210 bracket_counter = bracket_counter-1
|
ewan@6766
|
211 if bracket_counter==0:
|
ewan@6766
|
212 record_state=4
|
ewan@6766
|
213 else:
|
ewan@6766
|
214 param=param+tok
|
ewan@6766
|
215 # State 4: Look for doc string
|
ewan@6766
|
216 elif (s==4):
|
ewan@6766
|
217 if (type==token.NEWLINE or type==token.INDENT or type==token.SLASHEQUAL):
|
ewan@6766
|
218 return
|
ewan@6766
|
219 elif (tok==":"):
|
ewan@6766
|
220 return
|
ewan@6766
|
221 elif (type==token.STRING):
|
ewan@6766
|
222 while tok[:1]=='r' or tok[:1]=='u':
|
ewan@6766
|
223 tok=tok[1:]
|
ewan@6766
|
224 while tok[:1]=='"':
|
ewan@6766
|
225 tok=tok[1:]
|
ewan@6766
|
226 while tok[-1:]=='"':
|
ewan@6766
|
227 tok=tok[:-1]
|
ewan@6766
|
228 doc_string=tok
|
ewan@6766
|
229 record_state=0
|
ewan@6766
|
230
|
ewan@6766
|
231 ######################################################################
|
ewan@6766
|
232 # Starts the recording of a name & param part.
|
ewan@6766
|
233 # The function rec_name_n_param() has to be fed with tokens. After
|
ewan@6766
|
234 # the necessary tokens are fed the name and parameters can be found
|
ewan@6766
|
235 # in the global variables "name" und "param".
|
ewan@6766
|
236 ######################################################################
|
ewan@6766
|
237 def start_recording():
|
ewan@6766
|
238 global record_state,param,name, doc_string
|
ewan@6766
|
239 record_state=1
|
ewan@6766
|
240 name=""
|
ewan@6766
|
241 param=""
|
ewan@6766
|
242 doc_string=""
|
ewan@6766
|
243
|
ewan@6766
|
244 ######################################################################
|
ewan@6766
|
245 # Test if recording is finished
|
ewan@6766
|
246 ######################################################################
|
ewan@6766
|
247 def is_recording_finished():
|
ewan@6766
|
248 global record_state
|
ewan@6766
|
249 return record_state==0
|
ewan@6766
|
250
|
ewan@6766
|
251 ######################################################################
|
ewan@6766
|
252 ## Gather comment block
|
ewan@6766
|
253 ######################################################################
|
ewan@6766
|
254 def gather_comment(type,tok,spos):
|
ewan@6766
|
255 global comment_block,comment_finished
|
ewan@6766
|
256 if (type!=tokenize.COMMENT):
|
ewan@6766
|
257 comment_finished = 1
|
ewan@6766
|
258 else:
|
ewan@6766
|
259 # Output old comment block if a new one is started.
|
ewan@6766
|
260 if (comment_finished):
|
ewan@6766
|
261 print_comment(spos)
|
ewan@6766
|
262 comment_finished=0
|
ewan@6766
|
263 if (tok[0:2]=="##" and tok[0:3]!="###"):
|
ewan@6766
|
264 append_comment_lines(tok[2:])
|
ewan@6766
|
265
|
ewan@6766
|
266 ######################################################################
|
ewan@6766
|
267 ## Output comment block and empty buffer.
|
ewan@6766
|
268 ######################################################################
|
ewan@6766
|
269 def print_comment(spos):
|
ewan@6766
|
270 global comment_block,comment_finished
|
ewan@6766
|
271 if (comment_block!=[]):
|
ewan@6766
|
272 output("/** ",spos)
|
ewan@6766
|
273 for c in comment_block:
|
ewan@6766
|
274 output(c,spos)
|
ewan@6766
|
275 output("*/\n",spos)
|
ewan@6766
|
276 comment_block = []
|
ewan@6766
|
277 comment_finished = 0
|
ewan@6766
|
278
|
ewan@6766
|
279 ######################################################################
|
ewan@6766
|
280 def set_state(s):
|
ewan@6766
|
281 global stateStack
|
ewan@6766
|
282 stateStack[len(stateStack)-1]=s
|
ewan@6766
|
283
|
ewan@6766
|
284 ######################################################################
|
ewan@6766
|
285 def get_state():
|
ewan@6766
|
286 global stateStack
|
ewan@6766
|
287 return stateStack[len(stateStack)-1]
|
ewan@6766
|
288
|
ewan@6766
|
289 ######################################################################
|
ewan@6766
|
290 def push_state(s):
|
ewan@6766
|
291 global stateStack
|
ewan@6766
|
292 stateStack.append(s)
|
ewan@6766
|
293
|
ewan@6766
|
294 ######################################################################
|
ewan@6766
|
295 def pop_state():
|
ewan@6766
|
296 global stateStack
|
ewan@6766
|
297 stateStack.pop()
|
ewan@6766
|
298
|
ewan@6766
|
299
|
ewan@6766
|
300 ######################################################################
|
ewan@6766
|
301 def tok_eater(type, tok, spos, epos, line):
|
ewan@6766
|
302 global stateStack,name,param,class_spos,def_spos,import_spos
|
ewan@6766
|
303 global doc_string, modules, import_token, module_has_docstring
|
ewan@6766
|
304 global protection_level, private_member
|
ewan@6766
|
305 global out_row
|
ewan@6766
|
306
|
ewan@6766
|
307 while out_row + 1 < spos[0]:
|
ewan@6766
|
308 output("\n", (0, 0))
|
ewan@6766
|
309
|
ewan@6766
|
310 rec_name_n_param(type,tok)
|
ewan@6766
|
311 if (string.replace(string.strip(tok)," ","")=="##private:"):
|
ewan@6766
|
312 protection_level = "private"
|
ewan@6766
|
313 output("private:\n",spos)
|
ewan@6766
|
314 elif (string.replace(string.strip(tok)," ","")=="##protected:"):
|
ewan@6766
|
315 protection_level = "protected"
|
ewan@6766
|
316 output("protected:\n",spos)
|
ewan@6766
|
317 elif (string.replace(string.strip(tok)," ","")=="##public:"):
|
ewan@6766
|
318 protection_level = "public"
|
ewan@6766
|
319 output("public:\n",spos)
|
ewan@6766
|
320 else:
|
ewan@6766
|
321 gather_comment(type,tok,spos)
|
ewan@6766
|
322
|
ewan@6766
|
323 state = get_state()
|
ewan@6766
|
324
|
ewan@6766
|
325 # sys.stderr.write("%d: %s\n"%(state, tok))
|
ewan@6766
|
326
|
ewan@6766
|
327 # OUTSIDE
|
ewan@6766
|
328 if (state==OUTSIDE):
|
ewan@6766
|
329 if (tok=="class"):
|
ewan@6766
|
330 start_recording()
|
ewan@6766
|
331 class_spos = spos
|
ewan@6766
|
332 push_state(BUILD_CLASS_DECL)
|
ewan@6766
|
333 elif (tok=="def"):
|
ewan@6766
|
334 start_recording()
|
ewan@6766
|
335 def_spos = spos
|
ewan@6766
|
336 push_state(BUILD_DEF_DECL)
|
ewan@6766
|
337 elif (tok=="import") or (tok=="from"):
|
ewan@6766
|
338 import_token = tok
|
ewan@6766
|
339 import_spos = spos
|
ewan@6766
|
340 modules = []
|
ewan@6766
|
341 push_state(IMPORT)
|
ewan@6766
|
342 elif (spos[1] == 0 and tok[:3] == '"""'):
|
ewan@6766
|
343 # Capture module docstring as namespace documentation
|
ewan@6766
|
344 module_has_docstring = True
|
ewan@6766
|
345 append_comment_lines("\\namespace %s\n" % namespace)
|
ewan@6766
|
346 append_comment_lines(tok[3:-3])
|
ewan@6766
|
347 print_comment(spos)
|
ewan@6766
|
348
|
ewan@6766
|
349 # IMPORT
|
ewan@6766
|
350 elif (state==IMPORT):
|
ewan@6766
|
351 if (type==token.NAME):
|
ewan@6766
|
352 modules.append(tok)
|
ewan@6766
|
353 set_state(IMPORT_OP)
|
ewan@6766
|
354 # IMPORT_OP
|
ewan@6766
|
355 elif (state==IMPORT_OP):
|
ewan@6766
|
356 if (tok=="."):
|
ewan@6766
|
357 set_state(IMPORT_APPEND)
|
ewan@6766
|
358 elif (tok==","):
|
ewan@6766
|
359 set_state(IMPORT)
|
ewan@6766
|
360 else:
|
ewan@6766
|
361 for m in modules:
|
ewan@6766
|
362 output('#include "'+m.replace('.',os.path.sep)+'.py"\n', import_spos, immediate=1)
|
ewan@6766
|
363 if import_token=="from":
|
ewan@6766
|
364 output('using namespace '+m.replace('.', '::')+';\n', import_spos)
|
ewan@6766
|
365 pop_state()
|
ewan@6766
|
366 # IMPORT_APPEND
|
ewan@6766
|
367 elif (state==IMPORT_APPEND):
|
ewan@6766
|
368 if (type==token.NAME):
|
ewan@6766
|
369 modules[len(modules)-1]+="."+tok
|
ewan@6766
|
370 set_state(IMPORT_OP)
|
ewan@6766
|
371 # BUILD_CLASS_DECL
|
ewan@6766
|
372 elif (state==BUILD_CLASS_DECL):
|
ewan@6766
|
373 if (is_recording_finished()):
|
ewan@6766
|
374 s = "class "+name
|
ewan@6766
|
375 if (param!=""): s = s+" : public "+param.replace('.','::')
|
ewan@6766
|
376 if (doc_string!=""):
|
ewan@6766
|
377 append_comment_lines(doc_string)
|
ewan@6766
|
378 print_comment(class_spos)
|
ewan@6766
|
379 output(s+"\n",class_spos)
|
ewan@6766
|
380 output("{\n",(class_spos[0]+1,class_spos[1]))
|
ewan@6766
|
381 protection_level = "public"
|
ewan@6766
|
382 output(" public:\n",(class_spos[0]+2,class_spos[1]))
|
ewan@6766
|
383 set_state(BUILD_CLASS_BODY)
|
ewan@6766
|
384 # BUILD_CLASS_BODY
|
ewan@6766
|
385 elif (state==BUILD_CLASS_BODY):
|
ewan@6766
|
386 if (type!=token.INDENT and type!=token.NEWLINE and type!=40 and
|
ewan@6766
|
387 type!=tokenize.NL and type!=tokenize.COMMENT and
|
ewan@6766
|
388 (spos[1]<=class_spos[1])):
|
ewan@6766
|
389 output("}; // end of class\n",(out_row+1,class_spos[1]))
|
ewan@6766
|
390 pop_state()
|
ewan@6766
|
391 elif (tok=="def"):
|
ewan@6766
|
392 start_recording()
|
ewan@6766
|
393 def_spos = spos
|
ewan@6766
|
394 push_state(BUILD_DEF_DECL)
|
ewan@6766
|
395 # BUILD_DEF_DECL
|
ewan@6766
|
396 elif (state==BUILD_DEF_DECL):
|
ewan@6766
|
397 if (is_recording_finished()):
|
ewan@6766
|
398 param = param.replace("\n", " ")
|
ewan@6766
|
399 param = param.replace("=", " = ")
|
ewan@6766
|
400 params = param.split(",")
|
ewan@6766
|
401 if BUILD_CLASS_BODY in stateStack:
|
ewan@6766
|
402 if len(name) > 1 \
|
ewan@6766
|
403 and name[0:2] == '__' \
|
ewan@6766
|
404 and name[len(name)-2:len(name)] != '__' \
|
ewan@6766
|
405 and protection_level != 'private':
|
ewan@6766
|
406 private_member = True
|
ewan@6766
|
407 output(" private:\n",(def_spos[0]+2,def_spos[1]))
|
ewan@6766
|
408
|
ewan@6766
|
409 if (doc_string != ""):
|
ewan@6766
|
410 append_comment_lines(doc_string)
|
ewan@6766
|
411
|
ewan@6766
|
412 print_comment(def_spos)
|
ewan@6766
|
413
|
ewan@6766
|
414 output_function_decl(name, params)
|
ewan@6766
|
415 # output("{\n",(def_spos[0]+1,def_spos[1]))
|
ewan@6766
|
416 set_state(BUILD_DEF_BODY)
|
ewan@6766
|
417 # BUILD_DEF_BODY
|
ewan@6766
|
418 elif (state==BUILD_DEF_BODY):
|
ewan@6766
|
419 if (type!=token.INDENT and type!=token.NEWLINE \
|
ewan@6766
|
420 and type!=40 and type!=tokenize.NL \
|
ewan@6766
|
421 and (spos[1]<=def_spos[1])):
|
ewan@6766
|
422 # output("} // end of method/function\n",(out_row+1,def_spos[1]))
|
ewan@6766
|
423 if private_member and protection_level != 'private':
|
ewan@6766
|
424 private_member = False
|
ewan@6766
|
425 output(" " + protection_level + ":\n",(def_spos[0]+2,def_spos[1]))
|
ewan@6766
|
426 pop_state()
|
ewan@6766
|
427 # else:
|
ewan@6766
|
428 # output(tok,spos)
|
ewan@6766
|
429
|
ewan@6766
|
430
|
ewan@6766
|
431 def output_function_decl(name, params):
|
ewan@6766
|
432 global def_spos
|
ewan@6766
|
433
|
ewan@6766
|
434 # Do we document a class method? then remove the 'self' parameter
|
ewan@6766
|
435 if params[0] == 'self':
|
ewan@6766
|
436 preamble = ''
|
ewan@6766
|
437 params = params[1:]
|
ewan@6766
|
438 else:
|
ewan@6766
|
439 preamble = 'static '
|
ewan@6766
|
440 if params[0] == 'cls':
|
ewan@6766
|
441 params = params[1:]
|
ewan@6766
|
442
|
ewan@6766
|
443 param_string = string.join(params, ", Type ")
|
ewan@6766
|
444
|
ewan@6766
|
445 if param_string == '':
|
ewan@6766
|
446 param_string = '(' + param_string + ');\n'
|
ewan@6766
|
447 else:
|
ewan@6766
|
448 param_string = '(Type ' + param_string + ');\n'
|
ewan@6766
|
449
|
ewan@6766
|
450 output(preamble, def_spos)
|
ewan@6766
|
451 output(name, def_spos)
|
ewan@6766
|
452 output(param_string, def_spos)
|
ewan@6766
|
453
|
ewan@6766
|
454
|
ewan@6766
|
455 def append_comment_lines(lines):
|
ewan@6766
|
456 map(append_comment_line, doc_string.split('\n'))
|
ewan@6766
|
457
|
ewan@6766
|
458 paramRE = re.compile(r'(@param \w+):')
|
ewan@6766
|
459
|
ewan@6766
|
460 def append_comment_line(line):
|
ewan@6766
|
461 global paramRE
|
ewan@6766
|
462
|
ewan@6766
|
463 comment_block.append(paramRE.sub(r'\1', line) + '\n')
|
ewan@6766
|
464
|
ewan@6766
|
465 def dump(filename):
|
ewan@6766
|
466 f = open(filename)
|
ewan@6766
|
467 r = f.readlines()
|
ewan@6766
|
468 for s in r:
|
ewan@6766
|
469 sys.stdout.write(s)
|
ewan@6766
|
470
|
ewan@6766
|
471 def filter(filename):
|
ewan@6766
|
472 global name, module_has_docstring
|
ewan@6766
|
473
|
ewan@6766
|
474 path,name = os.path.split(filename)
|
ewan@6766
|
475 root,ext = os.path.splitext(name)
|
ewan@6766
|
476
|
ewan@6766
|
477 output("namespace "+root+" {\n",(0,0))
|
ewan@6766
|
478
|
ewan@6766
|
479 # set module name for tok_eater to use if there's a module doc string
|
ewan@6766
|
480 name = root
|
ewan@6766
|
481
|
ewan@6766
|
482 # sys.stderr.write('Filtering "'+filename+'"...')
|
ewan@6766
|
483 f = open(filename)
|
ewan@6766
|
484 tokenize.tokenize(f.readline, tok_eater)
|
ewan@6766
|
485 f.close()
|
ewan@6766
|
486 print_comment((0,0))
|
ewan@6766
|
487
|
ewan@6766
|
488 output("\n",(0,0))
|
ewan@6766
|
489 output("} // end of namespace\n",(0,0))
|
ewan@6766
|
490
|
ewan@6766
|
491 if not module_has_docstring:
|
ewan@6766
|
492 # Put in default namespace documentation
|
ewan@6766
|
493 output('/** \\namespace '+root+' \n',(0,0))
|
ewan@6766
|
494 output(' \\brief Module "%s" */\n'%(root),(0,0))
|
ewan@6766
|
495
|
ewan@6766
|
496 for s in outbuffer:
|
ewan@6766
|
497 outfile.write(s)
|
ewan@6766
|
498
|
ewan@6766
|
499
|
ewan@6766
|
500 def filterFile(filename, out=sys.stdout):
|
ewan@6766
|
501 global outfile
|
ewan@6766
|
502
|
ewan@6766
|
503 outfile = out
|
ewan@6766
|
504
|
ewan@6766
|
505 try:
|
ewan@6766
|
506 root,ext = os.path.splitext(filename)
|
ewan@6766
|
507
|
ewan@6766
|
508 if ext==".py":
|
ewan@6766
|
509 filter(filename)
|
ewan@6766
|
510 else:
|
ewan@6766
|
511 dump(filename)
|
ewan@6766
|
512
|
ewan@6766
|
513 # sys.stderr.write("OK\n")
|
ewan@6766
|
514 except IOError,e:
|
ewan@6766
|
515 sys.stderr.write(e[1]+"\n")
|
ewan@6766
|
516
|
ewan@6766
|
517
|
ewan@6766
|
518 ######################################################################
|
ewan@6766
|
519
|
ewan@6766
|
520 # preparePath
|
ewan@6766
|
521 def preparePath(path):
|
ewan@6766
|
522 """Prepare a path.
|
ewan@6766
|
523
|
ewan@6766
|
524 Checks if the path exists and creates it if it does not exist.
|
ewan@6766
|
525 """
|
ewan@6766
|
526 if not os.path.exists(path):
|
ewan@6766
|
527 parent = os.path.dirname(path)
|
ewan@6766
|
528 if parent!="":
|
ewan@6766
|
529 preparePath(parent)
|
ewan@6766
|
530 os.mkdir(path)
|
ewan@6766
|
531
|
ewan@6766
|
532 # isNewer
|
ewan@6766
|
533 def isNewer(file1,file2):
|
ewan@6766
|
534 """Check if file1 is newer than file2.
|
ewan@6766
|
535
|
ewan@6766
|
536 file1 must be an existing file.
|
ewan@6766
|
537 """
|
ewan@6766
|
538 if not os.path.exists(file2):
|
ewan@6766
|
539 return True
|
ewan@6766
|
540 return os.stat(file1)[ST_MTIME]>os.stat(file2)[ST_MTIME]
|
ewan@6766
|
541
|
ewan@6766
|
542 # convert
|
ewan@6766
|
543 def convert(srcpath, destpath):
|
ewan@6766
|
544 """Convert a Python source tree into a C+ stub tree.
|
ewan@6766
|
545
|
ewan@6766
|
546 All *.py files in srcpath (including sub-directories) are filtered
|
ewan@6766
|
547 and written to destpath. If destpath exists, only the files
|
ewan@6766
|
548 that have been modified are filtered again. Files that were deleted
|
ewan@6766
|
549 from srcpath are also deleted in destpath if they are still present.
|
ewan@6766
|
550 The function returns the number of processed *.py files.
|
ewan@6766
|
551 """
|
ewan@6766
|
552 count=0
|
ewan@6766
|
553 sp = os.path.join(srcpath,"*")
|
ewan@6766
|
554 sfiles = glob.glob(sp)
|
ewan@6766
|
555 dp = os.path.join(destpath,"*")
|
ewan@6766
|
556 dfiles = glob.glob(dp)
|
ewan@6766
|
557 leftovers={}
|
ewan@6766
|
558 for df in dfiles:
|
ewan@6766
|
559 leftovers[os.path.basename(df)]=1
|
ewan@6766
|
560
|
ewan@6766
|
561 for srcfile in sfiles:
|
ewan@6766
|
562 basename = os.path.basename(srcfile)
|
ewan@6766
|
563 if basename in leftovers:
|
ewan@6766
|
564 del leftovers[basename]
|
ewan@6766
|
565
|
ewan@6766
|
566 # Is it a subdirectory?
|
ewan@6766
|
567 if os.path.isdir(srcfile):
|
ewan@6766
|
568 sdir = os.path.join(srcpath,basename)
|
ewan@6766
|
569 ddir = os.path.join(destpath,basename)
|
ewan@6766
|
570 count+=convert(sdir, ddir)
|
ewan@6766
|
571 continue
|
ewan@6766
|
572 # Check the extension (only *.py will be converted)
|
ewan@6766
|
573 root, ext = os.path.splitext(srcfile)
|
ewan@6766
|
574 if ext.lower()!=".py":
|
ewan@6766
|
575 continue
|
ewan@6766
|
576
|
ewan@6766
|
577 destfile = os.path.join(destpath,basename)
|
ewan@6766
|
578 if destfile==srcfile:
|
ewan@6766
|
579 print "WARNING: Input and output names are identical!"
|
ewan@6766
|
580 sys.exit(1)
|
ewan@6766
|
581
|
ewan@6766
|
582 count+=1
|
ewan@6766
|
583 # sys.stdout.write("%s\015"%(srcfile))
|
ewan@6766
|
584
|
ewan@6766
|
585 if isNewer(srcfile, destfile):
|
ewan@6766
|
586 preparePath(os.path.dirname(destfile))
|
ewan@6766
|
587 # out=open(destfile,"w")
|
ewan@6766
|
588 # filterFile(srcfile, out)
|
ewan@6766
|
589 # out.close()
|
ewan@6766
|
590 os.system("python %s -f %s>%s"%(sys.argv[0],srcfile,destfile))
|
ewan@6766
|
591
|
ewan@6766
|
592 # Delete obsolete files in destpath
|
ewan@6766
|
593 for df in leftovers:
|
ewan@6766
|
594 dname=os.path.join(destpath,df)
|
ewan@6766
|
595 if os.path.isdir(dname):
|
ewan@6766
|
596 try:
|
ewan@6766
|
597 shutil.rmtree(dname)
|
ewan@6766
|
598 except:
|
ewan@6766
|
599 print "Can't remove obsolete directory '%s'"%dname
|
ewan@6766
|
600 else:
|
ewan@6766
|
601 try:
|
ewan@6766
|
602 os.remove(dname)
|
ewan@6766
|
603 except:
|
ewan@6766
|
604 print "Can't remove obsolete file '%s'"%dname
|
ewan@6766
|
605
|
ewan@6766
|
606 return count
|
ewan@6766
|
607
|
ewan@6766
|
608
|
ewan@6766
|
609 ######################################################################
|
ewan@6766
|
610 ######################################################################
|
ewan@6766
|
611 ######################################################################
|
ewan@6766
|
612
|
ewan@6766
|
613 filter_file = False
|
ewan@6766
|
614
|
ewan@6766
|
615 try:
|
ewan@6766
|
616 opts, args = getopt.getopt(sys.argv[1:], "hf", ["help"])
|
ewan@6766
|
617 except getopt.GetoptError,e:
|
ewan@6766
|
618 print e
|
ewan@6766
|
619 sys.exit(1)
|
ewan@6766
|
620
|
ewan@6766
|
621 for o,a in opts:
|
ewan@6766
|
622 if o=="-f":
|
ewan@6766
|
623 filter_file = True
|
ewan@6766
|
624
|
ewan@6766
|
625 if filter_file:
|
ewan@6766
|
626 # Filter the specified file and print the result to stdout
|
ewan@6766
|
627 filename = string.join(args)
|
ewan@6766
|
628 filterFile(filename)
|
ewan@6766
|
629 else:
|
ewan@6766
|
630
|
ewan@6766
|
631 if len(args)!=2:
|
ewan@6766
|
632 sys.stderr.write("%s options input output\n"%(os.path.basename(sys.argv[0])))
|
ewan@6766
|
633 sys.exit(1)
|
ewan@6766
|
634
|
ewan@6766
|
635 # Filter an entire Python source tree
|
ewan@6766
|
636 print '"%s" -> "%s"\n'%(args[0],args[1])
|
ewan@6766
|
637 c=convert(args[0],args[1])
|
ewan@6766
|
638 print "%d files"%(c)
|
ewan@6766
|
639
|