1 # Copyright (C) 2005 OPEN CASCADE, CEA, EDF R&D, LEG
2 # PRINCIPIA R&D, EADS CCR, Lip6, BV, CEDRAT
3 # This library is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU Lesser General Public
5 # License as published by the Free Software Foundation; either
6 # version 2.1 of the License.
8 # This library is distributed in the hope that it will be useful
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 # Lesser General Public License for more details.
13 # You should have received a copy of the GNU Lesser General Public
14 # License along with this library; if not, write to the Free Software
15 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
51 # Variables used by rec_name_n_param()
63 # Which import was used? ("import" or "from")
66 # Comment block buffer
74 stateStack = [OUTSIDE]
76 # Keep track of whether module has a docstring
77 module_has_docstring = False
79 # Keep track of member protection
80 protection_level = "public"
81 private_member = False
83 # Keep track of the module namespace
86 ######################################################################
87 # Output string s. '\n' may only be at the end of the string (not
88 # somewhere in the middle).
92 ######################################################################
93 def output(s,spos, immediate=0):
94 global outbuffer, out_row, out_col, outfile
96 os = string.rjust(s,spos[1]-out_col+len(s))
105 out_col = spos[1]+len(s)
108 ######################################################################
109 # Records a name and parameters. The name is either a class name or
110 # a function name. Then the parameter is either the base class or
111 # the function parameters.
112 # The name is stored in the global variable "name", the parameters
114 # The variable "record_state" holds the current state of this internal
116 # The recording is started by calling start_recording().
119 ######################################################################
120 def rec_name_n_param(type, tok):
121 global record_state,name,param,doc_string,bracket_counter
123 # State 0: Do nothing.
126 # State 1: Remember name.
130 # State 2: Wait for opening bracket or colon
135 if (tok==':'): record_state=4
136 # State 3: Store parameter (or base class) and wait for an ending bracket
138 if (tok=='*' or tok=='**'):
141 bracket_counter = bracket_counter+1
143 bracket_counter = bracket_counter-1
144 if bracket_counter==0:
148 # State 4: Look for doc string
150 if (type==token.NEWLINE or type==token.INDENT or type==token.SLASHEQUAL):
154 elif (type==token.STRING):
155 while tok[:1]=='r' or tok[:1]=='u':
164 ######################################################################
165 # Starts the recording of a name & param part.
166 # The function rec_name_n_param() has to be fed with tokens. After
167 # the necessary tokens are fed the name and parameters can be found
168 # in the global variables "name" und "param".
169 ######################################################################
170 def start_recording():
171 global record_state,param,name, doc_string
177 ######################################################################
178 # Test if recording is finished
179 ######################################################################
180 def is_recording_finished():
182 return record_state==0
184 ######################################################################
185 ## Gather comment block
186 ######################################################################
187 def gather_comment(type,tok,spos):
188 global comment_block,comment_finished
189 if (type!=tokenize.COMMENT):
192 # Output old comment block if a new one is started.
193 if (comment_finished):
196 if (tok[0:2]=="##" and tok[0:3]!="###"):
197 comment_block.append(tok[2:])
199 ######################################################################
200 ## Output comment block and empty buffer.
201 ######################################################################
202 def print_comment(spos):
203 global comment_block,comment_finished
204 if (comment_block!=[]):
206 for c in comment_block:
212 ######################################################################
215 stateStack[len(stateStack)-1]=s
217 ######################################################################
220 return stateStack[len(stateStack)-1]
222 ######################################################################
227 ######################################################################
233 ######################################################################
234 def tok_eater(type, tok, spos, epos, line):
235 global stateStack,name,param,class_spos,def_spos,import_spos
236 global doc_string, modules, import_token, module_has_docstring
237 global protection_level, private_member
239 rec_name_n_param(type,tok)
240 if (string.replace(string.strip(tok)," ","")=="##private:"):
241 protection_level = "private"
242 output("private:\n",spos)
243 elif (string.replace(string.strip(tok)," ","")=="##protected:"):
244 protection_level = "protected"
245 output("protected:\n",spos)
246 elif (string.replace(string.strip(tok)," ","")=="##public:"):
247 protection_level = "public"
248 output("public:\n",spos)
250 gather_comment(type,tok,spos)
254 # sys.stderr.write("%d: %s\n"%(state, tok))
261 push_state(BUILD_CLASS_DECL)
265 push_state(BUILD_DEF_DECL)
266 elif (tok=="import") or (tok=="from"):
271 elif (spos[1] == 0 and tok[:3] == '"""'):
272 # Capture module docstring as namespace documentation
273 module_has_docstring = True
274 #comment_block.append("\\namespace %s\n" % namespace)
275 comment_block.append(tok[3:-3])
279 elif (state==IMPORT):
280 if (type==token.NAME):
284 elif (state==IMPORT_OP):
286 set_state(IMPORT_APPEND)
291 output('#include "'+m.replace('.',os.sep)+'.py"\n', import_spos, immediate=1)
292 if import_token=="from":
293 output('using namespace '+m.replace('.', '::')+';\n', import_spos)
296 elif (state==IMPORT_APPEND):
297 if (type==token.NAME):
298 modules[len(modules)-1]+="."+tok
301 elif (state==BUILD_CLASS_DECL):
302 if (is_recording_finished()):
304 if (param!=""): s = s+" : public "+param.replace('.','::')
305 if (doc_string!=""): comment_block.append(doc_string)
306 print_comment(class_spos)
307 output(s+"\n",class_spos)
308 output("{\n",(class_spos[0]+1,class_spos[1]))
309 protection_level = "public"
310 output(" public:\n",(class_spos[0]+2,class_spos[1]))
311 set_state(BUILD_CLASS_BODY)
313 elif (state==BUILD_CLASS_BODY):
314 if (type!=token.INDENT and type!=token.NEWLINE and type!=40 and
315 type!=tokenize.NL and type!=tokenize.COMMENT and
316 (spos[1]<=class_spos[1])):
317 output("}; // end of class\n",(out_row+1,class_spos[1]))
322 push_state(BUILD_DEF_DECL)
324 elif (state==BUILD_DEF_DECL):
325 if (is_recording_finished()):
327 # Do we document a class method? then remove the 'self' parameter
328 if BUILD_CLASS_BODY in stateStack:
329 params = param.split(",")
330 if params[0] == 'self':
331 param = string.join(params[1:], ",")
334 if params[0] == 'cls':
335 param = string.join(params[1:], ",")
336 s = s+name+"("+param+");\n"
338 and name[0:2] == '__' \
339 and name[len(name)-2:len(name)] != '__' \
340 and protection_level != 'private':
341 private_member = True
342 output(" private:\n",(def_spos[0]+2,def_spos[1]))
344 s = name+"("+param+");\n"
345 if (doc_string!=""): comment_block.append(doc_string)
346 print_comment(def_spos)
348 # output("{\n",(def_spos[0]+1,def_spos[1]))
349 set_state(BUILD_DEF_BODY)
351 elif (state==BUILD_DEF_BODY):
352 if (type!=token.INDENT and type!=token.NEWLINE \
353 and type!=40 and type!=tokenize.NL \
354 and (spos[1]<=def_spos[1])):
355 # output("} // end of method/function\n",(out_row+1,def_spos[1]))
356 if private_member and protection_level != 'private':
357 private_member = False
358 output(" " + protection_level + ":\n",(def_spos[0]+2,def_spos[1]))
370 def filter(filename):
371 global name, module_has_docstring
372 global namespace,outbuffer
375 path,name = os.path.split(filename)
376 root,ext = os.path.splitext(name)
380 if root == "__init__":
383 root=namespace+"::"+root
387 output("namespace "+root+" {\n",(0,0))
389 # set module name for tok_eater to use if there's a module doc string
394 sys.stderr.write("namespace: "+namespace+'\n')
395 sys.stderr.write("root: "+root+'\n')
396 sys.stderr.write('Filtering "'+filename+'"...')
399 tokenize.tokenize(f.readline, tok_eater)
404 output("} // end of namespace\n",(0,0))
406 if not module_has_docstring:
407 # Put in default namespace documentation
408 output('/** \\namespace '+root+' \n',(0,0))
409 output(' \\brief Module "%s" */\n'%(root),(0,0))
414 module_has_docstring = False
417 def filterFile(filename, out=sys.stdout):
423 root,ext = os.path.splitext(filename)
430 sys.stderr.write("OK\n")
432 sys.stderr.write(e[1]+"\n")
435 ######################################################################
438 def preparePath(path):
441 Checks if the path exists and creates it if it does not exist.
443 if not os.path.exists(path):
444 parent = os.path.dirname(path)
450 def isNewer(file1,file2):
451 """Check if file1 is newer than file2.
453 file1 must be an existing file.
455 if not os.path.exists(file2):
457 return os.stat(file1)[ST_MTIME]>os.stat(file2)[ST_MTIME]
460 def convert(srcpath, destpath):
461 """Convert a Python source tree into a C+ stub tree.
463 All *.py files in srcpath (including sub-directories) are filtered
464 and written to destpath. If destpath exists, only the files
465 that have been modified are filtered again. Files that were deleted
466 from srcpath are also deleted in destpath if they are still present.
467 The function returns the number of processed *.py files.
471 l=os.listdir(srcpath)
472 if "__init__.py" in l:
474 namespace=namespace+"::"+os.path.split(srcpath)[1]
476 namespace=os.path.split(srcpath)[1]
477 print "It's a package:",namespace
478 sp = os.path.join(srcpath,"*")
479 sfiles = glob.glob(sp)
480 dp = os.path.join(destpath,"*")
481 dfiles = glob.glob(dp)
484 leftovers[os.path.basename(df)]=1
486 for srcfile in sfiles:
487 basename = os.path.basename(srcfile)
488 if basename in leftovers:
489 del leftovers[basename]
491 # Is it a subdirectory?
492 if os.path.isdir(srcfile):
494 sdir = os.path.join(srcpath,basename)
495 ddir = os.path.join(destpath,basename)
496 count+=convert(sdir, ddir)
499 # Check the extension (only *.py will be converted)
500 root, ext = os.path.splitext(srcfile)
501 if ext.lower()!=".py":
504 destfile = os.path.join(destpath,basename)
505 if destfile==srcfile:
506 print "WARNING: Input and output names are identical!"
510 # sys.stdout.write("%s\015"%(srcfile))
512 if isNewer(srcfile, destfile):
513 preparePath(os.path.dirname(destfile))
514 out=open(destfile,"w")
515 filterFile(srcfile, out)
517 # os.system("python %s -f %s>%s"%(sys.argv[0],srcfile,destfile))
519 # Delete obsolete files in destpath
521 dname=os.path.join(destpath,df)
522 if os.path.isdir(dname):
526 print "Can't remove obsolete directory '%s'"%dname
531 print "Can't remove obsolete file '%s'"%dname
536 ######################################################################
537 ######################################################################
538 ######################################################################
543 opts, args = getopt.getopt(sys.argv[1:], "hf", ["help"])
544 except getopt.GetoptError,e:
553 # Filter the specified file and print the result to stdout
554 filename = string.join(args)
559 sys.stderr.write("%s options input output\n"%(os.path.basename(sys.argv[0])))
562 # Filter an entire Python source tree
563 print '"%s" -> "%s"\n'%(args[0],args[1])
564 c=convert(args[0],args[1])