1 # Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE
3 # Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 # CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
6 # This library is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU Lesser General Public
8 # License as published by the Free Software Foundation; either
9 # version 2.1 of the License.
11 # This library is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 # Lesser General Public License for more details.
16 # You should have received a copy of the GNU Lesser General Public
17 # License along with this library; if not, write to the Free Software
18 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
54 # Variables used by rec_name_n_param()
66 # Which import was used? ("import" or "from")
69 # Comment block buffer
77 stateStack = [OUTSIDE]
79 # Keep track of whether module has a docstring
80 module_has_docstring = False
82 # Keep track of member protection
83 protection_level = "public"
84 private_member = False
86 # Keep track of the module namespace
89 ######################################################################
90 # Output string s. '\n' may only be at the end of the string (not
91 # somewhere in the middle).
95 ######################################################################
96 def output(s,spos, immediate=0):
97 global outbuffer, out_row, out_col, outfile
99 os = string.rjust(s,spos[1]-out_col+len(s))
108 out_col = spos[1]+len(s)
111 ######################################################################
112 # Records a name and parameters. The name is either a class name or
113 # a function name. Then the parameter is either the base class or
114 # the function parameters.
115 # The name is stored in the global variable "name", the parameters
117 # The variable "record_state" holds the current state of this internal
119 # The recording is started by calling start_recording().
122 ######################################################################
123 def rec_name_n_param(type, tok):
124 global record_state,name,param,doc_string,bracket_counter
126 # State 0: Do nothing.
129 # State 1: Remember name.
133 # State 2: Wait for opening bracket or colon
138 if (tok==':'): record_state=4
139 # State 3: Store parameter (or base class) and wait for an ending bracket
141 if (tok=='*' or tok=='**'):
144 bracket_counter = bracket_counter+1
146 bracket_counter = bracket_counter-1
147 if bracket_counter==0:
151 # State 4: Look for doc string
153 if (type==token.NEWLINE or type==token.INDENT or type==token.SLASHEQUAL):
157 elif (type==token.STRING):
158 while tok[:1]=='r' or tok[:1]=='u':
167 ######################################################################
168 # Starts the recording of a name & param part.
169 # The function rec_name_n_param() has to be fed with tokens. After
170 # the necessary tokens are fed the name and parameters can be found
171 # in the global variables "name" und "param".
172 ######################################################################
173 def start_recording():
174 global record_state,param,name, doc_string
180 ######################################################################
181 # Test if recording is finished
182 ######################################################################
183 def is_recording_finished():
185 return record_state==0
187 ######################################################################
188 ## Gather comment block
189 ######################################################################
190 def gather_comment(type,tok,spos):
191 global comment_block,comment_finished
192 if (type!=tokenize.COMMENT):
195 # Output old comment block if a new one is started.
196 if (comment_finished):
199 if (tok[0:2]=="##" and tok[0:3]!="###"):
200 comment_block.append(tok[2:])
202 ######################################################################
203 ## Output comment block and empty buffer.
204 ######################################################################
205 def print_comment(spos):
206 global comment_block,comment_finished
207 if (comment_block!=[]):
209 for c in comment_block:
215 ######################################################################
218 stateStack[len(stateStack)-1]=s
220 ######################################################################
223 return stateStack[len(stateStack)-1]
225 ######################################################################
230 ######################################################################
236 ######################################################################
237 def tok_eater(type, tok, spos, epos, line):
238 global stateStack,name,param,class_spos,def_spos,import_spos
239 global doc_string, modules, import_token, module_has_docstring
240 global protection_level, private_member
242 rec_name_n_param(type,tok)
243 if (string.replace(string.strip(tok)," ","")=="##private:"):
244 protection_level = "private"
245 output("private:\n",spos)
246 elif (string.replace(string.strip(tok)," ","")=="##protected:"):
247 protection_level = "protected"
248 output("protected:\n",spos)
249 elif (string.replace(string.strip(tok)," ","")=="##public:"):
250 protection_level = "public"
251 output("public:\n",spos)
253 gather_comment(type,tok,spos)
257 # sys.stderr.write("%d: %s\n"%(state, tok))
264 push_state(BUILD_CLASS_DECL)
268 push_state(BUILD_DEF_DECL)
269 elif (tok=="import") or (tok=="from"):
274 elif (spos[1] == 0 and tok[:3] == '"""'):
275 # Capture module docstring as namespace documentation
276 module_has_docstring = True
277 #comment_block.append("\\namespace %s\n" % namespace)
278 comment_block.append(tok[3:-3])
282 elif (state==IMPORT):
283 if (type==token.NAME):
287 elif (state==IMPORT_OP):
289 set_state(IMPORT_APPEND)
294 output('#include "'+m.replace('.',os.sep)+'.py"\n', import_spos, immediate=1)
295 if import_token=="from":
296 output('using namespace '+m.replace('.', '::')+';\n', import_spos)
299 elif (state==IMPORT_APPEND):
300 if (type==token.NAME):
301 modules[len(modules)-1]+="."+tok
304 elif (state==BUILD_CLASS_DECL):
305 if (is_recording_finished()):
307 if (param!=""): s = s+" : public "+param.replace('.','::')
308 if (doc_string!=""): comment_block.append(doc_string)
309 print_comment(class_spos)
310 output(s+"\n",class_spos)
311 output("{\n",(class_spos[0]+1,class_spos[1]))
312 protection_level = "public"
313 output(" public:\n",(class_spos[0]+2,class_spos[1]))
314 set_state(BUILD_CLASS_BODY)
316 elif (state==BUILD_CLASS_BODY):
317 if (type!=token.INDENT and type!=token.NEWLINE and type!=40 and
318 type!=tokenize.NL and type!=tokenize.COMMENT and
319 (spos[1]<=class_spos[1])):
320 output("}; // end of class\n",(out_row+1,class_spos[1]))
325 push_state(BUILD_DEF_DECL)
327 elif (state==BUILD_DEF_DECL):
328 if (is_recording_finished()):
330 # Do we document a class method? then remove the 'self' parameter
331 if BUILD_CLASS_BODY in stateStack:
332 params = param.split(",")
333 if params[0] == 'self':
334 param = string.join(params[1:], ",")
337 if params[0] == 'cls':
338 param = string.join(params[1:], ",")
339 s = s+name+"("+param+");\n"
341 and name[0:2] == '__' \
342 and name[len(name)-2:len(name)] != '__' \
343 and protection_level != 'private':
344 private_member = True
345 output(" private:\n",(def_spos[0]+2,def_spos[1]))
347 s = name+"("+param+");\n"
348 if (doc_string!=""): comment_block.append(doc_string)
349 print_comment(def_spos)
351 # output("{\n",(def_spos[0]+1,def_spos[1]))
352 set_state(BUILD_DEF_BODY)
354 elif (state==BUILD_DEF_BODY):
355 if (type!=token.INDENT and type!=token.NEWLINE \
356 and type!=40 and type!=tokenize.NL \
357 and (spos[1]<=def_spos[1])):
358 # output("} // end of method/function\n",(out_row+1,def_spos[1]))
359 if private_member and protection_level != 'private':
360 private_member = False
361 output(" " + protection_level + ":\n",(def_spos[0]+2,def_spos[1]))
373 def filter(filename):
374 global name, module_has_docstring
375 global namespace,outbuffer
378 path,name = os.path.split(filename)
379 root,ext = os.path.splitext(name)
383 if root == "__init__":
386 root=namespace+"::"+root
390 output("namespace "+root+" {\n",(0,0))
392 # set module name for tok_eater to use if there's a module doc string
397 sys.stderr.write("namespace: "+namespace+'\n')
398 sys.stderr.write("root: "+root+'\n')
399 sys.stderr.write('Filtering "'+filename+'"...')
402 tokenize.tokenize(f.readline, tok_eater)
407 output("} // end of namespace\n",(0,0))
409 if not module_has_docstring:
410 # Put in default namespace documentation
411 output('/** \\namespace '+root+' \n',(0,0))
412 output(' \\brief Module "%s" */\n'%(root),(0,0))
417 module_has_docstring = False
420 def filterFile(filename, out=sys.stdout):
426 root,ext = os.path.splitext(filename)
433 sys.stderr.write("OK\n")
435 sys.stderr.write(e[1]+"\n")
438 ######################################################################
441 def preparePath(path):
444 Checks if the path exists and creates it if it does not exist.
446 if not os.path.exists(path):
447 parent = os.path.dirname(path)
453 def isNewer(file1,file2):
454 """Check if file1 is newer than file2.
456 file1 must be an existing file.
458 if not os.path.exists(file2):
460 return os.stat(file1)[ST_MTIME]>os.stat(file2)[ST_MTIME]
463 def convert(srcpath, destpath):
464 """Convert a Python source tree into a C+ stub tree.
466 All *.py files in srcpath (including sub-directories) are filtered
467 and written to destpath. If destpath exists, only the files
468 that have been modified are filtered again. Files that were deleted
469 from srcpath are also deleted in destpath if they are still present.
470 The function returns the number of processed *.py files.
474 l=os.listdir(srcpath)
475 if "__init__.py" in l:
477 namespace=namespace+"::"+os.path.split(srcpath)[1]
479 namespace=os.path.split(srcpath)[1]
480 print "It's a package:",namespace
481 sp = os.path.join(srcpath,"*")
482 sfiles = glob.glob(sp)
483 dp = os.path.join(destpath,"*")
484 dfiles = glob.glob(dp)
487 leftovers[os.path.basename(df)]=1
489 for srcfile in sfiles:
490 basename = os.path.basename(srcfile)
491 if basename in leftovers:
492 del leftovers[basename]
494 # Is it a subdirectory?
495 if os.path.isdir(srcfile):
497 sdir = os.path.join(srcpath,basename)
498 ddir = os.path.join(destpath,basename)
499 count+=convert(sdir, ddir)
502 # Check the extension (only *.py will be converted)
503 root, ext = os.path.splitext(srcfile)
504 if ext.lower()!=".py":
507 destfile = os.path.join(destpath,basename)
508 if destfile==srcfile:
509 print "WARNING: Input and output names are identical!"
513 # sys.stdout.write("%s\015"%(srcfile))
515 if isNewer(srcfile, destfile):
516 preparePath(os.path.dirname(destfile))
517 out=open(destfile,"w")
518 filterFile(srcfile, out)
520 # os.system("python %s -f %s>%s"%(sys.argv[0],srcfile,destfile))
522 # Delete obsolete files in destpath
524 dname=os.path.join(destpath,df)
525 if os.path.isdir(dname):
529 print "Can't remove obsolete directory '%s'"%dname
534 print "Can't remove obsolete file '%s'"%dname
539 ######################################################################
540 ######################################################################
541 ######################################################################
546 opts, args = getopt.getopt(sys.argv[1:], "hf", ["help"])
547 except getopt.GetoptError,e:
556 # Filter the specified file and print the result to stdout
557 filename = string.join(args)
562 sys.stderr.write("%s options input output\n"%(os.path.basename(sys.argv[0])))
565 # Filter an entire Python source tree
566 print '"%s" -> "%s"\n'%(args[0],args[1])
567 c=convert(args[0],args[1])