|
1 # -*- coding: utf-8 -*- |
|
2 |
|
3 # Copyright (c) 2005 - 2019 Detlev Offenbach <detlev@die-offenbachs.de> |
|
4 # |
|
5 |
|
6 """ |
|
7 Parse a Python file and retrieve classes, functions/methods and attributes. |
|
8 |
|
9 Parse enough of a Python file to recognize class and method definitions and |
|
10 to find out the superclasses of a class as well as its attributes. |
|
11 |
|
12 This is module is based on pyclbr found in the Python 2.2.2 distribution. |
|
13 """ |
|
14 |
|
15 |
|
16 from __future__ import unicode_literals |
|
17 |
|
18 import sys |
|
19 import imp |
|
20 import re |
|
21 |
|
22 import Utilities |
|
23 import Utilities.ClassBrowsers as ClassBrowsers |
|
24 from . import ClbrBaseClasses |
|
25 from functools import reduce |
|
26 |
|
27 TABWIDTH = 4 |
|
28 |
|
29 SUPPORTED_TYPES = [ClassBrowsers.PY_SOURCE, ClassBrowsers.PTL_SOURCE] |
|
30 |
|
31 _getnext = re.compile( |
|
32 r""" |
|
33 (?P<String> |
|
34 \# .*? $ # ignore everything in comments |
|
35 | |
|
36 \""" [^"\\]* (?: |
|
37 (?: \\. | "(?!"") ) |
|
38 [^"\\]* |
|
39 )* |
|
40 \""" |
|
41 |
|
42 | ''' [^'\\]* (?: |
|
43 (?: \\. | '(?!'') ) |
|
44 [^'\\]* |
|
45 )* |
|
46 ''' |
|
47 |
|
48 | " [^"\\\n]* (?: \\. [^"\\\n]*)* " |
|
49 |
|
50 | ' [^'\\\n]* (?: \\. [^'\\\n]*)* ' |
|
51 ) |
|
52 |
|
53 | (?P<Publics> |
|
54 ^ |
|
55 [ \t]* __all__ [ \t]* = [ \t]* \[ |
|
56 (?P<Identifiers> [^\]]*? ) |
|
57 \] |
|
58 ) |
|
59 |
|
60 | (?P<MethodModifier> |
|
61 ^ |
|
62 (?P<MethodModifierIndent> [ \t]* ) |
|
63 (?P<MethodModifierType> @classmethod | @staticmethod ) |
|
64 ) |
|
65 |
|
66 | (?P<Method> |
|
67 ^ |
|
68 (?P<MethodIndent> [ \t]* ) |
|
69 (?: async [ \t]+ )? def [ \t]+ |
|
70 (?P<MethodName> \w+ ) |
|
71 (?: [ \t]* \[ (?: plain | html ) \] )? |
|
72 [ \t]* \( |
|
73 (?P<MethodSignature> (?: [^)] | \)[ \t]*,? )*? ) |
|
74 \) [ \t]* |
|
75 (?P<MethodReturnAnnotation> (?: -> [ \t]* [^:]+ )? ) |
|
76 [ \t]* : |
|
77 ) |
|
78 |
|
79 | (?P<Class> |
|
80 ^ |
|
81 (?P<ClassIndent> [ \t]* ) |
|
82 class [ \t]+ |
|
83 (?P<ClassName> \w+ ) |
|
84 [ \t]* |
|
85 (?P<ClassSupers> \( [^)]* \) )? |
|
86 [ \t]* : |
|
87 ) |
|
88 |
|
89 | (?P<Attribute> |
|
90 ^ |
|
91 (?P<AttributeIndent> [ \t]* ) |
|
92 self [ \t]* \. [ \t]* |
|
93 (?P<AttributeName> \w+ ) |
|
94 [ \t]* = |
|
95 ) |
|
96 |
|
97 | (?P<Variable> |
|
98 ^ |
|
99 (?P<VariableIndent> [ \t]* ) |
|
100 (?P<VariableName> \w+ ) |
|
101 [ \t]* = |
|
102 ) |
|
103 |
|
104 | (?P<Main> |
|
105 ^ |
|
106 if \s+ __name__ \s* == \s* [^:]+ : $ |
|
107 ) |
|
108 |
|
109 | (?P<ConditionalDefine> |
|
110 ^ |
|
111 (?P<ConditionalDefineIndent> [ \t]* ) |
|
112 (?: (?: if | elif ) [ \t]+ [^:]* | else [ \t]* ) : |
|
113 (?= \s* (?: async [ \t]+ )? def) |
|
114 ) |
|
115 |
|
116 | (?P<Import> |
|
117 ^ [ \t]* (?: import | from [ \t]+ \. [ \t]+ import ) [ \t]+ |
|
118 (?P<ImportList> (?: [^#;\\\n]* (?: \\\n )* )* ) |
|
119 ) |
|
120 |
|
121 | (?P<ImportFrom> |
|
122 ^ [ \t]* from [ \t]+ |
|
123 (?P<ImportFromPath> |
|
124 \.* \w+ |
|
125 (?: |
|
126 [ \t]* \. [ \t]* \w+ |
|
127 )* |
|
128 ) |
|
129 [ \t]+ |
|
130 import [ \t]+ |
|
131 (?P<ImportFromList> |
|
132 (?: \( \s* .*? \s* \) ) |
|
133 | |
|
134 (?: [^#;\\\n]* (?: \\\n )* )* ) |
|
135 ) |
|
136 |
|
137 | (?P<CodingLine> |
|
138 ^ \# \s* [*_-]* \s* coding[:=] \s* (?P<Coding> [-\w_.]+ ) \s* [*_-]* $ |
|
139 )""", |
|
140 re.VERBOSE | re.DOTALL | re.MULTILINE).search |
|
141 |
|
142 _commentsub = re.compile(r"""#[^\n]*\n|#[^\n]*$""").sub |
|
143 |
|
144 _modules = {} # cache of modules we've seen |
|
145 |
|
146 |
|
147 class VisibilityMixin(ClbrBaseClasses.ClbrVisibilityMixinBase): |
|
148 """ |
|
149 Mixin class implementing the notion of visibility. |
|
150 """ |
|
151 def __init__(self): |
|
152 """ |
|
153 Constructor |
|
154 """ |
|
155 if self.name.startswith('__'): |
|
156 self.setPrivate() |
|
157 elif self.name.startswith('_'): |
|
158 self.setProtected() |
|
159 else: |
|
160 self.setPublic() |
|
161 |
|
162 |
|
163 class Class(ClbrBaseClasses.Class, VisibilityMixin): |
|
164 """ |
|
165 Class to represent a Python class. |
|
166 """ |
|
167 def __init__(self, module, name, superClasses, file, lineno): |
|
168 """ |
|
169 Constructor |
|
170 |
|
171 @param module name of the module containing this class |
|
172 @param name name of this class |
|
173 @param superClasses list of class names this class is inherited from |
|
174 @param file filename containing this class |
|
175 @param lineno linenumber of the class definition |
|
176 """ |
|
177 ClbrBaseClasses.Class.__init__(self, module, name, superClasses, file, |
|
178 lineno) |
|
179 VisibilityMixin.__init__(self) |
|
180 |
|
181 |
|
182 class Function(ClbrBaseClasses.Function, VisibilityMixin): |
|
183 """ |
|
184 Class to represent a Python function. |
|
185 """ |
|
186 def __init__(self, module, name, file, lineno, signature='', separator=',', |
|
187 modifierType=ClbrBaseClasses.Function.General, annotation=""): |
|
188 """ |
|
189 Constructor |
|
190 |
|
191 @param module name of the module containing this function |
|
192 @param name name of this function |
|
193 @param file filename containing this class |
|
194 @param lineno linenumber of the class definition |
|
195 @param signature parameterlist of the method |
|
196 @param separator string separating the parameters |
|
197 @param modifierType type of the function |
|
198 @param annotation return annotation |
|
199 """ |
|
200 ClbrBaseClasses.Function.__init__(self, module, name, file, lineno, |
|
201 signature, separator, modifierType, |
|
202 annotation) |
|
203 VisibilityMixin.__init__(self) |
|
204 |
|
205 |
|
206 class Attribute(ClbrBaseClasses.Attribute, VisibilityMixin): |
|
207 """ |
|
208 Class to represent a class attribute. |
|
209 """ |
|
210 def __init__(self, module, name, file, lineno): |
|
211 """ |
|
212 Constructor |
|
213 |
|
214 @param module name of the module containing this class |
|
215 @param name name of this class |
|
216 @param file filename containing this attribute |
|
217 @param lineno linenumber of the class definition |
|
218 """ |
|
219 ClbrBaseClasses.Attribute.__init__(self, module, name, file, lineno) |
|
220 VisibilityMixin.__init__(self) |
|
221 |
|
222 |
|
223 class Publics(object): |
|
224 """ |
|
225 Class to represent the list of public identifiers. |
|
226 """ |
|
227 def __init__(self, module, file, lineno, idents): |
|
228 """ |
|
229 Constructor |
|
230 |
|
231 @param module name of the module containing this function |
|
232 @param file filename containing this class |
|
233 @param lineno linenumber of the class definition |
|
234 @param idents list of public identifiers |
|
235 """ |
|
236 self.module = module |
|
237 self.name = '__all__' |
|
238 self.file = file |
|
239 self.lineno = lineno |
|
240 self.identifiers = [e.replace('"', '').replace("'", "").strip() |
|
241 for e in idents.split(',')] |
|
242 |
|
243 |
|
244 class Imports(object): |
|
245 """ |
|
246 Class to represent the list of imported modules. |
|
247 """ |
|
248 def __init__(self, module, file): |
|
249 """ |
|
250 Constructor |
|
251 |
|
252 @param module name of the module containing the import (string) |
|
253 @param file file name containing the import (string) |
|
254 """ |
|
255 self.module = module |
|
256 self.name = 'import' |
|
257 self.file = file |
|
258 self.imports = {} |
|
259 |
|
260 def addImport(self, moduleName, names, lineno): |
|
261 """ |
|
262 Public method to add a list of imported names. |
|
263 |
|
264 @param moduleName name of the imported module (string) |
|
265 @param names list of names (list of strings) |
|
266 @param lineno line number of the import |
|
267 """ |
|
268 if moduleName not in self.imports: |
|
269 module = ImportedModule(self.module, self.file, moduleName) |
|
270 self.imports[moduleName] = module |
|
271 else: |
|
272 module = self.imports[moduleName] |
|
273 module.addImport(lineno, names) |
|
274 |
|
275 def getImport(self, moduleName): |
|
276 """ |
|
277 Public method to get an imported module item. |
|
278 |
|
279 @param moduleName name of the imported module (string) |
|
280 @return imported module item (ImportedModule) or None |
|
281 """ |
|
282 if moduleName in self.imports: |
|
283 return self.imports[moduleName] |
|
284 else: |
|
285 return None |
|
286 |
|
287 def getImports(self): |
|
288 """ |
|
289 Public method to get all imported module names. |
|
290 |
|
291 @return dictionary of imported module names with name as key and list |
|
292 of line numbers of imports as value |
|
293 """ |
|
294 return self.imports |
|
295 |
|
296 |
|
297 class ImportedModule(object): |
|
298 """ |
|
299 Class to represent an imported module. |
|
300 """ |
|
301 def __init__(self, module, file, importedModule): |
|
302 """ |
|
303 Constructor |
|
304 |
|
305 @param module name of the module containing the import (string) |
|
306 @param file file name containing the import (string) |
|
307 @param importedModule name of the imported module (string) |
|
308 """ |
|
309 self.module = module |
|
310 self.name = 'import' |
|
311 self.file = file |
|
312 self.importedModuleName = importedModule |
|
313 self.linenos = [] |
|
314 self.importedNames = {} |
|
315 # dictionary of imported names with name as key and list of line |
|
316 # numbers as value |
|
317 |
|
318 def addImport(self, lineno, importedNames): |
|
319 """ |
|
320 Public method to add a list of imported names. |
|
321 |
|
322 @param lineno line number of the import |
|
323 @param importedNames list of imported names (list of strings) |
|
324 """ |
|
325 if lineno not in self.linenos: |
|
326 self.linenos.append(lineno) |
|
327 |
|
328 for name in importedNames: |
|
329 if name not in self.importedNames: |
|
330 self.importedNames[name] = [lineno] |
|
331 else: |
|
332 self.importedNames[name].append(lineno) |
|
333 |
|
334 |
|
335 def readmodule_ex(module, path=None, inpackage=False, isPyFile=False): |
|
336 """ |
|
337 Read a module file and return a dictionary of classes. |
|
338 |
|
339 Search for MODULE in PATH and sys.path, read and parse the |
|
340 module and return a dictionary with one entry for each class |
|
341 found in the module. |
|
342 |
|
343 @param module name of the module file (string) |
|
344 @param path path the module should be searched in (list of strings) |
|
345 @param inpackage flag indicating a module inside a package is scanned |
|
346 @param isPyFile flag indicating a Python file (boolean) |
|
347 @return the resulting dictionary |
|
348 """ |
|
349 global _modules |
|
350 |
|
351 dictionary = {} |
|
352 dict_counts = {} |
|
353 |
|
354 if module in _modules: |
|
355 # we've seen this module before... |
|
356 return _modules[module] |
|
357 if module in sys.builtin_module_names: |
|
358 # this is a built-in module |
|
359 _modules[module] = dictionary |
|
360 return dictionary |
|
361 |
|
362 # search the path for the module |
|
363 path = [] if path is None else path[:] |
|
364 f = None |
|
365 if inpackage: |
|
366 try: |
|
367 f, file, (suff, mode, type) = \ |
|
368 ClassBrowsers.find_module(module, path) |
|
369 except ImportError: |
|
370 f = None |
|
371 if f is None: |
|
372 fullpath = path[:] + sys.path[:] |
|
373 f, file, (suff, mode, type) = \ |
|
374 ClassBrowsers.find_module(module, fullpath, isPyFile) |
|
375 if module.endswith(".py") and type == imp.PKG_DIRECTORY: |
|
376 return dictionary |
|
377 if type == imp.PKG_DIRECTORY: |
|
378 dictionary['__path__'] = [file] |
|
379 _modules[module] = dictionary |
|
380 path = [file] + path |
|
381 f, file, (suff, mode, type) = \ |
|
382 ClassBrowsers.find_module('__init__', [file]) |
|
383 if f: |
|
384 f.close() |
|
385 if type not in SUPPORTED_TYPES: |
|
386 # not Python source, can't do anything with this module |
|
387 _modules[module] = dictionary |
|
388 return dictionary |
|
389 |
|
390 _modules[module] = dictionary |
|
391 classstack = [] # stack of (class, indent) pairs |
|
392 conditionalsstack = [] # stack of indents of conditional defines |
|
393 deltastack = [] |
|
394 deltaindent = 0 |
|
395 deltaindentcalculated = 0 |
|
396 try: |
|
397 src = Utilities.readEncodedFile(file)[0] |
|
398 except (UnicodeError, IOError): |
|
399 # can't do anything with this module |
|
400 _modules[module] = dictionary |
|
401 return dictionary |
|
402 |
|
403 lineno, last_lineno_pos = 1, 0 |
|
404 lastGlobalEntry = None |
|
405 cur_obj = None |
|
406 i = 0 |
|
407 modifierType = ClbrBaseClasses.Function.General |
|
408 modifierIndent = -1 |
|
409 while True: |
|
410 m = _getnext(src, i) |
|
411 if not m: |
|
412 break |
|
413 start, i = m.span() |
|
414 |
|
415 if m.start("MethodModifier") >= 0: |
|
416 modifierIndent = _indent(m.group("MethodModifierIndent")) |
|
417 modifierType = m.group("MethodModifierType") |
|
418 |
|
419 elif m.start("Method") >= 0: |
|
420 # found a method definition or function |
|
421 thisindent = _indent(m.group("MethodIndent")) |
|
422 meth_name = m.group("MethodName") |
|
423 meth_sig = m.group("MethodSignature") |
|
424 meth_sig = meth_sig.replace('\\\n', '') |
|
425 meth_sig = _commentsub('', meth_sig) |
|
426 meth_ret = m.group("MethodReturnAnnotation") |
|
427 meth_ret = meth_ret.replace('\\\n', '') |
|
428 meth_ret = _commentsub('', meth_ret) |
|
429 lineno = lineno + src.count('\n', last_lineno_pos, start) |
|
430 last_lineno_pos = start |
|
431 if modifierType and modifierIndent == thisindent: |
|
432 if modifierType == "@staticmethod": |
|
433 modifier = ClbrBaseClasses.Function.Static |
|
434 elif modifierType == "@classmethod": |
|
435 modifier = ClbrBaseClasses.Function.Class |
|
436 else: |
|
437 modifier = ClbrBaseClasses.Function.General |
|
438 else: |
|
439 modifier = ClbrBaseClasses.Function.General |
|
440 # modify indentation level for conditional defines |
|
441 if conditionalsstack: |
|
442 if thisindent > conditionalsstack[-1]: |
|
443 if not deltaindentcalculated: |
|
444 deltastack.append(thisindent - conditionalsstack[-1]) |
|
445 deltaindent = reduce(lambda x, y: x + y, deltastack) |
|
446 deltaindentcalculated = 1 |
|
447 thisindent -= deltaindent |
|
448 else: |
|
449 while conditionalsstack and \ |
|
450 conditionalsstack[-1] >= thisindent: |
|
451 del conditionalsstack[-1] |
|
452 if deltastack: |
|
453 del deltastack[-1] |
|
454 deltaindentcalculated = 0 |
|
455 # close all classes indented at least as much |
|
456 while classstack and \ |
|
457 classstack[-1][1] >= thisindent: |
|
458 if classstack[-1][0] is not None: |
|
459 # record the end line |
|
460 classstack[-1][0].setEndLine(lineno - 1) |
|
461 del classstack[-1] |
|
462 if classstack: |
|
463 # it's a class method |
|
464 cur_class = classstack[-1][0] |
|
465 if cur_class: |
|
466 # it's a method/nested def |
|
467 f = Function(None, meth_name, |
|
468 file, lineno, meth_sig, annotation=meth_ret, |
|
469 modifierType=modifier) |
|
470 cur_class._addmethod(meth_name, f) |
|
471 else: |
|
472 # it's a function |
|
473 f = Function(module, meth_name, |
|
474 file, lineno, meth_sig, annotation=meth_ret, |
|
475 modifierType=modifier) |
|
476 if meth_name in dict_counts: |
|
477 dict_counts[meth_name] += 1 |
|
478 meth_name = "{0}_{1:d}".format( |
|
479 meth_name, dict_counts[meth_name]) |
|
480 else: |
|
481 dict_counts[meth_name] = 0 |
|
482 dictionary[meth_name] = f |
|
483 if not classstack: |
|
484 if lastGlobalEntry: |
|
485 lastGlobalEntry.setEndLine(lineno - 1) |
|
486 lastGlobalEntry = f |
|
487 if cur_obj and isinstance(cur_obj, Function): |
|
488 cur_obj.setEndLine(lineno - 1) |
|
489 cur_obj = f |
|
490 classstack.append((f, thisindent)) # Marker for nested fns |
|
491 |
|
492 # reset the modifier settings |
|
493 modifierType = ClbrBaseClasses.Function.General |
|
494 modifierIndent = -1 |
|
495 |
|
496 elif m.start("String") >= 0: |
|
497 pass |
|
498 |
|
499 elif m.start("Class") >= 0: |
|
500 # we found a class definition |
|
501 thisindent = _indent(m.group("ClassIndent")) |
|
502 # close all classes indented at least as much |
|
503 while classstack and \ |
|
504 classstack[-1][1] >= thisindent: |
|
505 if classstack[-1][0] is not None: |
|
506 # record the end line |
|
507 classstack[-1][0].setEndLine(lineno - 1) |
|
508 del classstack[-1] |
|
509 lineno = lineno + src.count('\n', last_lineno_pos, start) |
|
510 last_lineno_pos = start |
|
511 class_name = m.group("ClassName") |
|
512 inherit = m.group("ClassSupers") |
|
513 if inherit: |
|
514 # the class inherits from other classes |
|
515 inherit = inherit[1:-1].strip() |
|
516 inherit = _commentsub('', inherit) |
|
517 names = [] |
|
518 for n in inherit.split(','): |
|
519 n = n.strip() |
|
520 if n in dictionary: |
|
521 # we know this super class |
|
522 n = dictionary[n] |
|
523 else: |
|
524 c = n.split('.') |
|
525 if len(c) > 1: |
|
526 # super class |
|
527 # is of the |
|
528 # form module.class: |
|
529 # look in |
|
530 # module for class |
|
531 m = c[-2] |
|
532 c = c[-1] |
|
533 if m in _modules: |
|
534 d = _modules[m] |
|
535 if c in d: |
|
536 n = d[c] |
|
537 names.append(n) |
|
538 inherit = names |
|
539 # remember this class |
|
540 cur_class = Class(module, class_name, inherit, |
|
541 file, lineno) |
|
542 if not classstack: |
|
543 if class_name in dict_counts: |
|
544 dict_counts[class_name] += 1 |
|
545 class_name = "{0}_{1:d}".format( |
|
546 class_name, dict_counts[class_name]) |
|
547 else: |
|
548 dict_counts[class_name] = 0 |
|
549 dictionary[class_name] = cur_class |
|
550 else: |
|
551 classstack[-1][0]._addclass(class_name, cur_class) |
|
552 if not classstack: |
|
553 if lastGlobalEntry: |
|
554 lastGlobalEntry.setEndLine(lineno - 1) |
|
555 lastGlobalEntry = cur_class |
|
556 classstack.append((cur_class, thisindent)) |
|
557 |
|
558 elif m.start("Attribute") >= 0: |
|
559 lineno = lineno + src.count('\n', last_lineno_pos, start) |
|
560 last_lineno_pos = start |
|
561 index = -1 |
|
562 while index >= -len(classstack): |
|
563 if classstack[index][0] is not None and \ |
|
564 not isinstance(classstack[index][0], Function): |
|
565 attr = Attribute( |
|
566 module, m.group("AttributeName"), file, lineno) |
|
567 classstack[index][0]._addattribute(attr) |
|
568 break |
|
569 else: |
|
570 index -= 1 |
|
571 |
|
572 elif m.start("Main") >= 0: |
|
573 # 'main' part of the script, reset class stack |
|
574 lineno = lineno + src.count('\n', last_lineno_pos, start) |
|
575 last_lineno_pos = start |
|
576 classstack = [] |
|
577 |
|
578 elif m.start("Variable") >= 0: |
|
579 thisindent = _indent(m.group("VariableIndent")) |
|
580 variable_name = m.group("VariableName") |
|
581 lineno = lineno + src.count('\n', last_lineno_pos, start) |
|
582 last_lineno_pos = start |
|
583 if thisindent == 0 or not classstack: |
|
584 # global variable, reset class stack first |
|
585 classstack = [] |
|
586 |
|
587 if "@@Globals@@" not in dictionary: |
|
588 dictionary["@@Globals@@"] = ClbrBaseClasses.ClbrBase( |
|
589 module, "Globals", file, lineno) |
|
590 dictionary["@@Globals@@"]._addglobal( |
|
591 Attribute(module, variable_name, file, lineno)) |
|
592 if lastGlobalEntry: |
|
593 lastGlobalEntry.setEndLine(lineno - 1) |
|
594 lastGlobalEntry = None |
|
595 else: |
|
596 index = -1 |
|
597 while index >= -len(classstack): |
|
598 if classstack[index][1] >= thisindent: |
|
599 index -= 1 |
|
600 else: |
|
601 if isinstance(classstack[index][0], Class): |
|
602 classstack[index][0]._addglobal( |
|
603 Attribute(module, variable_name, file, lineno)) |
|
604 break |
|
605 |
|
606 elif m.start("Publics") >= 0: |
|
607 idents = m.group("Identifiers") |
|
608 lineno = lineno + src.count('\n', last_lineno_pos, start) |
|
609 last_lineno_pos = start |
|
610 pubs = Publics(module, file, lineno, idents) |
|
611 dictionary['__all__'] = pubs |
|
612 |
|
613 elif m.start("Import") >= 0: |
|
614 # import module |
|
615 names = [n.strip() for n in |
|
616 "".join(m.group("ImportList").splitlines()) |
|
617 .replace("\\", "").split(',')] |
|
618 lineno = lineno + src.count('\n', last_lineno_pos, start) |
|
619 last_lineno_pos = start |
|
620 if "@@Import@@" not in dictionary: |
|
621 dictionary["@@Import@@"] = Imports(module, file) |
|
622 for name in names: |
|
623 dictionary["@@Import@@"].addImport(name, [], lineno) |
|
624 |
|
625 elif m.start("ImportFrom") >= 0: |
|
626 # from module import stuff |
|
627 mod = m.group("ImportFromPath") |
|
628 namesLines = (m.group("ImportFromList") |
|
629 .replace("(", "").replace(")", "") |
|
630 .replace("\\", "") |
|
631 .strip().splitlines()) |
|
632 namesLines = [line.split("#")[0].strip() |
|
633 for line in namesLines] |
|
634 names = [n.strip() for n in |
|
635 "".join(namesLines) |
|
636 .split(',')] |
|
637 lineno = lineno + src.count('\n', last_lineno_pos, start) |
|
638 last_lineno_pos = start |
|
639 if "@@Import@@" not in dictionary: |
|
640 dictionary["@@Import@@"] = Imports(module, file) |
|
641 dictionary["@@Import@@"].addImport(mod, names, lineno) |
|
642 |
|
643 elif m.start("ConditionalDefine") >= 0: |
|
644 # a conditional function/method definition |
|
645 thisindent = _indent(m.group("ConditionalDefineIndent")) |
|
646 while conditionalsstack and \ |
|
647 conditionalsstack[-1] >= thisindent: |
|
648 del conditionalsstack[-1] |
|
649 if deltastack: |
|
650 del deltastack[-1] |
|
651 conditionalsstack.append(thisindent) |
|
652 deltaindentcalculated = 0 |
|
653 |
|
654 elif m.start("CodingLine") >= 0: |
|
655 # a coding statement |
|
656 coding = m.group("Coding") |
|
657 lineno = lineno + src.count('\n', last_lineno_pos, start) |
|
658 last_lineno_pos = start |
|
659 if "@@Coding@@" not in dictionary: |
|
660 dictionary["@@Coding@@"] = ClbrBaseClasses.Coding( |
|
661 module, file, lineno, coding) |
|
662 |
|
663 else: |
|
664 assert 0, "regexp _getnext found something unexpected" |
|
665 |
|
666 if '__all__' in dictionary: |
|
667 # set visibility of all top level elements |
|
668 pubs = dictionary['__all__'] |
|
669 for key in dictionary.keys(): |
|
670 if key == '__all__' or key.startswith("@@"): |
|
671 continue |
|
672 if key in pubs.identifiers: |
|
673 dictionary[key].setPublic() |
|
674 else: |
|
675 dictionary[key].setPrivate() |
|
676 del dictionary['__all__'] |
|
677 |
|
678 return dictionary |
|
679 |
|
680 |
|
681 def _indent(ws): |
|
682 """ |
|
683 Module function to return the indentation depth. |
|
684 |
|
685 @param ws the whitespace to be checked (string) |
|
686 @return length of the whitespace string (integer) |
|
687 """ |
|
688 return len(ws.expandtabs(TABWIDTH)) |