xen-vtx-unstable

annotate docs/pythfilter.py @ 6781:8ca0f98ba8e2

merge?
author cl349@firebug.cl.cam.ac.uk
date Tue Sep 13 15:33:37 2005 +0000 (2005-09-13)
parents 4d899a738d59
children 72e4e2aab342
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@6768 472 global name, module_has_docstring, source_root
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@6768 477 if source_root and path.find(source_root) == 0:
ewan@6768 478 path = path[len(source_root):]
ewan@6768 479
ewan@6768 480 if path[0] == os.sep:
ewan@6768 481 path = path[1:]
ewan@6768 482
ewan@6768 483 ns = path.split(os.sep)
ewan@6768 484 else:
ewan@6768 485 ns = []
ewan@6768 486
ewan@6768 487 ns.append(root)
ewan@6768 488
ewan@6768 489 for n in ns:
ewan@6768 490 output("namespace " + n + " {\n",(0,0))
ewan@6766 491
ewan@6766 492 # set module name for tok_eater to use if there's a module doc string
ewan@6766 493 name = root
ewan@6766 494
ewan@6766 495 # sys.stderr.write('Filtering "'+filename+'"...')
ewan@6766 496 f = open(filename)
ewan@6766 497 tokenize.tokenize(f.readline, tok_eater)
ewan@6766 498 f.close()
ewan@6766 499 print_comment((0,0))
ewan@6766 500
ewan@6766 501 output("\n",(0,0))
ewan@6768 502
ewan@6768 503 for n in ns:
ewan@6768 504 output("} // end of namespace\n",(0,0))
ewan@6766 505
ewan@6766 506 if not module_has_docstring:
ewan@6766 507 # Put in default namespace documentation
ewan@6766 508 output('/** \\namespace '+root+' \n',(0,0))
ewan@6766 509 output(' \\brief Module "%s" */\n'%(root),(0,0))
ewan@6766 510
ewan@6766 511 for s in outbuffer:
ewan@6766 512 outfile.write(s)
ewan@6766 513
ewan@6766 514
ewan@6766 515 def filterFile(filename, out=sys.stdout):
ewan@6766 516 global outfile
ewan@6766 517
ewan@6766 518 outfile = out
ewan@6766 519
ewan@6766 520 try:
ewan@6766 521 root,ext = os.path.splitext(filename)
ewan@6766 522
ewan@6766 523 if ext==".py":
ewan@6766 524 filter(filename)
ewan@6766 525 else:
ewan@6766 526 dump(filename)
ewan@6766 527
ewan@6766 528 # sys.stderr.write("OK\n")
ewan@6766 529 except IOError,e:
ewan@6766 530 sys.stderr.write(e[1]+"\n")
ewan@6766 531
ewan@6766 532
ewan@6766 533 ######################################################################
ewan@6766 534
ewan@6766 535 # preparePath
ewan@6766 536 def preparePath(path):
ewan@6766 537 """Prepare a path.
ewan@6766 538
ewan@6766 539 Checks if the path exists and creates it if it does not exist.
ewan@6766 540 """
ewan@6766 541 if not os.path.exists(path):
ewan@6766 542 parent = os.path.dirname(path)
ewan@6766 543 if parent!="":
ewan@6766 544 preparePath(parent)
ewan@6766 545 os.mkdir(path)
ewan@6766 546
ewan@6766 547 # isNewer
ewan@6766 548 def isNewer(file1,file2):
ewan@6766 549 """Check if file1 is newer than file2.
ewan@6766 550
ewan@6766 551 file1 must be an existing file.
ewan@6766 552 """
ewan@6766 553 if not os.path.exists(file2):
ewan@6766 554 return True
ewan@6766 555 return os.stat(file1)[ST_MTIME]>os.stat(file2)[ST_MTIME]
ewan@6766 556
ewan@6766 557 # convert
ewan@6766 558 def convert(srcpath, destpath):
ewan@6766 559 """Convert a Python source tree into a C+ stub tree.
ewan@6766 560
ewan@6766 561 All *.py files in srcpath (including sub-directories) are filtered
ewan@6766 562 and written to destpath. If destpath exists, only the files
ewan@6766 563 that have been modified are filtered again. Files that were deleted
ewan@6766 564 from srcpath are also deleted in destpath if they are still present.
ewan@6766 565 The function returns the number of processed *.py files.
ewan@6766 566 """
ewan@6766 567 count=0
ewan@6766 568 sp = os.path.join(srcpath,"*")
ewan@6766 569 sfiles = glob.glob(sp)
ewan@6766 570 dp = os.path.join(destpath,"*")
ewan@6766 571 dfiles = glob.glob(dp)
ewan@6766 572 leftovers={}
ewan@6766 573 for df in dfiles:
ewan@6766 574 leftovers[os.path.basename(df)]=1
ewan@6766 575
ewan@6766 576 for srcfile in sfiles:
ewan@6766 577 basename = os.path.basename(srcfile)
ewan@6766 578 if basename in leftovers:
ewan@6766 579 del leftovers[basename]
ewan@6766 580
ewan@6766 581 # Is it a subdirectory?
ewan@6766 582 if os.path.isdir(srcfile):
ewan@6766 583 sdir = os.path.join(srcpath,basename)
ewan@6766 584 ddir = os.path.join(destpath,basename)
ewan@6766 585 count+=convert(sdir, ddir)
ewan@6766 586 continue
ewan@6766 587 # Check the extension (only *.py will be converted)
ewan@6766 588 root, ext = os.path.splitext(srcfile)
ewan@6766 589 if ext.lower()!=".py":
ewan@6766 590 continue
ewan@6766 591
ewan@6766 592 destfile = os.path.join(destpath,basename)
ewan@6766 593 if destfile==srcfile:
ewan@6766 594 print "WARNING: Input and output names are identical!"
ewan@6766 595 sys.exit(1)
ewan@6766 596
ewan@6766 597 count+=1
ewan@6766 598 # sys.stdout.write("%s\015"%(srcfile))
ewan@6766 599
ewan@6766 600 if isNewer(srcfile, destfile):
ewan@6766 601 preparePath(os.path.dirname(destfile))
ewan@6766 602 # out=open(destfile,"w")
ewan@6766 603 # filterFile(srcfile, out)
ewan@6766 604 # out.close()
ewan@6766 605 os.system("python %s -f %s>%s"%(sys.argv[0],srcfile,destfile))
ewan@6766 606
ewan@6766 607 # Delete obsolete files in destpath
ewan@6766 608 for df in leftovers:
ewan@6766 609 dname=os.path.join(destpath,df)
ewan@6766 610 if os.path.isdir(dname):
ewan@6766 611 try:
ewan@6766 612 shutil.rmtree(dname)
ewan@6766 613 except:
ewan@6766 614 print "Can't remove obsolete directory '%s'"%dname
ewan@6766 615 else:
ewan@6766 616 try:
ewan@6766 617 os.remove(dname)
ewan@6766 618 except:
ewan@6766 619 print "Can't remove obsolete file '%s'"%dname
ewan@6766 620
ewan@6766 621 return count
ewan@6766 622
ewan@6766 623
ewan@6766 624 ######################################################################
ewan@6766 625 ######################################################################
ewan@6766 626 ######################################################################
ewan@6766 627
ewan@6766 628 filter_file = False
ewan@6768 629 source_root = None
ewan@6766 630
ewan@6766 631 try:
ewan@6768 632 opts, args = getopt.getopt(sys.argv[1:], "hfr:", ["help"])
ewan@6766 633 except getopt.GetoptError,e:
ewan@6766 634 print e
ewan@6766 635 sys.exit(1)
ewan@6766 636
ewan@6766 637 for o,a in opts:
ewan@6766 638 if o=="-f":
ewan@6766 639 filter_file = True
ewan@6766 640
ewan@6768 641 if o=="-r":
ewan@6768 642 source_root = os.path.abspath(a)
ewan@6768 643
ewan@6766 644 if filter_file:
ewan@6766 645 # Filter the specified file and print the result to stdout
ewan@6766 646 filename = string.join(args)
ewan@6768 647 filterFile(os.path.abspath(filename))
ewan@6766 648 else:
ewan@6766 649
ewan@6766 650 if len(args)!=2:
ewan@6766 651 sys.stderr.write("%s options input output\n"%(os.path.basename(sys.argv[0])))
ewan@6766 652 sys.exit(1)
ewan@6766 653
ewan@6766 654 # Filter an entire Python source tree
ewan@6766 655 print '"%s" -> "%s"\n'%(args[0],args[1])
ewan@6766 656 c=convert(args[0],args[1])
ewan@6766 657 print "%d files"%(c)
ewan@6766 658