xen-vtx-unstable
annotate docs/pythfilter.py @ 6796:0d8c0db04258
Don't return failure when trying to delete a non-existent node.
Signed-off-by: Christian Limpach <Christian.Limpach@cl.cam.ac.uk>
Signed-off-by: Christian Limpach <Christian.Limpach@cl.cam.ac.uk>
author | cl349@firebug.cl.cam.ac.uk |
---|---|
date | Tue Sep 13 21:52:24 2005 +0000 (2005-09-13) |
parents | 72e4e2aab342 |
children |
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 |