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