1 from pycharm_generator_utils.util_methods import *
2 from pycharm_generator_utils.constants import *
6 class emptylistdict(dict):
7 """defaultdict not available before 2.5; simplest reimplementation using [] as default"""
9 def __getitem__(self, item):
11 return dict.__getitem__(self, item)
14 self.__setitem__(item, it)
18 """Buffers data in a list, can write to a file. Indentation is provided externally."""
20 def __init__(self, indenter):
22 self.indenter = indenter
26 self.data.append(ensureUnicode(data))
28 def out(self, indent, *what):
29 """Output the arguments, indenting as needed, and adding an eol"""
30 self.put(self.indenter.indent(indent))
35 def flush_bytes(self, outfile):
36 for data in self.data:
37 outfile.write(data.encode(OUT_ENCODING, "replace"))
39 def flush_str(self, outfile):
40 for data in self.data:
49 return len(self.data) == 0
52 #noinspection PyUnresolvedReferences,PyBroadException
53 class ModuleRedeclarator(object):
54 def __init__(self, module, outfile, mod_filename, indent_size=4, doing_builtins=False):
57 @param module module to restore.
58 @param outfile output file, must be open and writable.
59 @param mod_filename filename of binary module (the .dll or .so)
60 @param indent_size amount of space characters per indent
63 self.outfile = outfile # where we finally write
64 self.mod_filename = mod_filename
65 # we write things into buffers out-of-order
66 self.header_buf = Buf(self)
67 self.imports_buf = Buf(self)
68 self.functions_buf = Buf(self)
69 self.classes_buf = Buf(self)
70 self.footer_buf = Buf(self)
71 self.indent_size = indent_size
72 self._indent_step = " " * self.indent_size
74 self.imported_modules = {"": the_builtins} # explicit module imports: {"name": module}
75 self.hidden_imports = {} # {'real_mod_name': 'alias'}; we alias names with "__" since we don't want them exported
76 # ^ used for things that we don't re-export but need to import, e.g. certain base classes in gnome.
77 self._defined = {} # stores True for every name defined so far, to break circular refs in values
78 self.doing_builtins = doing_builtins
79 self.ret_type_cache = {}
80 self.used_imports = emptylistdict() # qual_mod_name -> [imported_names,..]: actually used imported names
82 def _initializeQApp(self):
83 try: # QtGui should be imported _before_ QtCore package.
84 # This is done for the QWidget references from QtCore (such as QSignalMapper). Known bug in PyQt 4.7+
85 # Causes "TypeError: C++ type 'QWidget*' is not supported as a native Qt signal type"
90 # manually instantiate and keep reference to singleton QCoreApplication (we don't want it to be deleted during the introspection)
91 # use QCoreApplication instead of QApplication to avoid blinking app in Dock on Mac OS
93 from PyQt4.QtCore import QCoreApplication
94 self.app = QCoreApplication([])
99 from PyQt5.QtCore import QCoreApplication
100 self.app = QCoreApplication([])
104 def indent(self, level):
105 """Return indentation whitespace for given level."""
106 return self._indent_step * level
109 for buf in (self.header_buf, self.imports_buf, self.functions_buf, self.classes_buf, self.footer_buf):
110 buf.flush(self.outfile)
112 # Some builtin classes effectively change __init__ signature without overriding it.
113 # This callable serves as a placeholder to be replaced via REDEFINED_BUILTIN_SIGS
114 def fake_builtin_init(self):
115 pass # just a callable, sig doesn't matter
117 fake_builtin_init.__doc__ = object.__init__.__doc__ # this forces class's doc to be used instead
120 def find_imported_name(self, item):
122 Finds out how the item is represented in imported modules.
123 @param item what to check
124 @return qualified name (like "sys.stdin") or None
126 # TODO: return a pair, not a glued string
127 if not isinstance(item, SIMPLEST_TYPES):
128 for mname in self.imported_modules:
129 m = self.imported_modules[mname]
130 for inner_name in m.__dict__:
131 suspect = getattr(m, inner_name)
135 elif self.module is the_builtins: # don't short-circuit builtins
137 return mname + inner_name
146 def invent_initializer(self, a_type):
148 Returns an innocuous initializer expression for a_type, or "None"
150 for initializer_type, r in self._initializers:
151 if initializer_type == a_type:
153 # NOTE: here we could handle things like defaultdict, sets, etc if we wanted
157 def fmt_value(self, out, p_value, indent, prefix="", postfix="", as_name=None, seen_values=None):
159 Formats and outputs value (it occupies an entire line or several lines).
160 @param out function that does output (a Buf.out)
161 @param p_value the value.
162 @param indent indent level.
163 @param prefix text to print before the value
164 @param postfix text to print after the value
165 @param as_name hints which name are we trying to print; helps with circular refs.
166 @param seen_values a list of keys we've seen if we're processing a dict
168 SELF_VALUE = "<value is a self-reference, replaced by this string>"
169 ERR_VALUE = "<failed to retrieve the value>"
170 if isinstance(p_value, SIMPLEST_TYPES):
171 out(indent, prefix, reliable_repr(p_value), postfix)
173 if sys.platform == "cli":
176 imported_name = self.find_imported_name(p_value)
178 out(indent, prefix, imported_name, postfix)
179 # TODO: kind of self.used_imports[imported_name].append(p_value) but split imported_name
180 # else we could potentially return smth we did not otherwise import. but not likely.
182 if isinstance(p_value, (list, tuple)):
184 seen_values = [p_value]
185 if len(p_value) == 0:
186 out(indent, prefix, repr(p_value), postfix)
188 if isinstance(p_value, list):
189 lpar, rpar = "[", "]"
191 lpar, rpar = "(", ")"
192 out(indent, prefix, lpar)
193 for value in p_value:
194 if value in seen_values:
196 elif not isinstance(value, SIMPLEST_TYPES):
197 seen_values.append(value)
198 self.fmt_value(out, value, indent + 1, postfix=",", seen_values=seen_values)
199 out(indent, rpar, postfix)
200 elif isinstance(p_value, dict):
201 if len(p_value) == 0:
202 out(indent, prefix, repr(p_value), postfix)
205 seen_values = [p_value]
206 out(indent, prefix, "{")
207 keys = list(p_value.keys())
211 pass # unsortable keys happen, e,g, in py3k _ctypes
216 is_seen = value in seen_values
223 elif not isinstance(value, SIMPLEST_TYPES):
224 seen_values.append(value)
225 if isinstance(k, SIMPLEST_TYPES):
226 self.fmt_value(out, value, indent + 1, prefix=repr(k) + ": ", postfix=",",
227 seen_values=seen_values)
229 # both key and value need fancy formatting
230 self.fmt_value(out, k, indent + 1, postfix=": ", seen_values=seen_values)
231 self.fmt_value(out, value, indent + 2, seen_values=seen_values)
233 out(indent, "}", postfix)
234 else: # something else, maybe representable
235 # look up this value in the module.
236 if sys.platform == "cli":
237 out(indent, prefix, "None", postfix)
240 for inner_name in self.module.__dict__:
241 if self.module.__dict__[inner_name] is p_value:
242 found_name = inner_name
244 if self._defined.get(found_name, False):
245 out(indent, prefix, found_name, postfix)
247 # a forward / circular declaration happens
249 real_value = cleanup(repr(p_value))
251 if found_name == as_name:
252 notice = " # (!) real value is %r" % real_value
255 notice = " # (!) forward: %s, real value is %r" % (found_name, real_value)
256 if SANE_REPR_RE.match(real_value):
257 out(indent, prefix, real_value, postfix, notice)
260 notice = " # (!) real value is %r" % real_value
261 out(indent, prefix, "None", postfix, notice)
263 def get_ret_type(self, attr):
265 Returns a return type string as given by T_RETURN in tokens, or None
268 ret_type = RET_TYPE.get(attr, None)
271 thing = getattr(self.module, attr, None)
273 if not isinstance(thing, type) and is_callable(thing): # a function
274 return None # TODO: maybe divinate a return type; see pygame.mixer.Channel
276 # adds no noticeable slowdown, I did measure. dch.
277 for im_name, im_module in self.imported_modules.items():
278 cache_key = (im_name, attr)
279 cached = self.ret_type_cache.get(cache_key, None)
282 ret_type = getattr(im_module, attr, None)
284 if isinstance(ret_type, type):
285 # detect a constructor
286 constr_args = detect_constructor(ret_type)
287 if constr_args is None:
288 constr_args = "*(), **{}" # a silly catch-all constructor
289 reference = "%s(%s)" % (attr, constr_args)
290 elif is_callable(ret_type): # a function, classes are ruled out above
295 result = "%s.%s" % (im_name, reference)
298 self.ret_type_cache[cache_key] = result
300 # TODO: handle things like "[a, b,..] and (foo,..)"
304 SIG_DOC_NOTE = "restored from __doc__"
305 SIG_DOC_UNRELIABLY = "NOTE: unreliably restored from __doc__ "
307 def restore_by_docstring(self, signature_string, class_name, deco=None, ret_hint=None):
309 @param signature_string: parameter list extracted from the doc string.
310 @param class_name: name of the containing class, or None
311 @param deco: decorator to use
312 @param ret_hint: return type hint, if available
313 @return (reconstructed_spec, return_type, note) or (None, _, _) if failed.
315 action("restoring func %r of class %r", signature_string, class_name)
317 parsing_failed = False
321 tokens = paramSeqAndRest.parseString(signature_string, True)
325 if ret_t[0] is T_RETURN:
327 ret_type = self.get_ret_type(ret_name) or self.get_ret_type(ret_hint)
328 except ParseException:
329 # it did not parse completely; scavenge what we can
330 parsing_failed = True
333 # most unrestrictive parsing
334 tokens = paramSeq.parseString(signature_string, False)
335 except ParseException:
338 seq = transform_seq(tokens)
340 # add safe defaults for unparsed
342 doc_node = self.SIG_DOC_UNRELIABLY
344 double_starred = None
347 if one.startswith("**"):
349 elif one.startswith("*"):
353 if not double_starred:
354 seq.append("**kwargs")
356 doc_node = self.SIG_DOC_NOTE
358 # add 'self' if needed YYY
359 if class_name and (not seq or seq[0] != 'self'):
360 first_param = propose_first_param(deco)
362 seq.insert(0, first_param)
363 seq = make_names_unique(seq)
364 return (seq, ret_type, doc_node)
366 def parse_func_doc(self, func_doc, func_id, func_name, class_name, deco=None, sip_generated=False):
368 @param func_doc: __doc__ of the function.
369 @param func_id: name to look for as identifier of the function in docstring
370 @param func_name: name of the function.
371 @param class_name: name of the containing class, or None
372 @param deco: decorator to use
373 @return (reconstructed_spec, return_literal, note) or (None, _, _) if failed.
377 for part in func_doc.split('\n'):
378 signature = func_id + '('
379 i = part.find(signature)
381 overloads.append(part[i + len(signature):])
382 if len(overloads) > 1:
383 docstring_results = [self.restore_by_docstring(overload, class_name, deco) for overload in overloads]
385 for result in docstring_results:
387 if rt and rt not in ret_types:
390 ret_literal = " or ".join(ret_types)
393 param_lists = [result[0] for result in docstring_results]
394 spec = build_signature(func_name, restore_parameters_for_overloads(param_lists))
395 return (spec, ret_literal, "restored from __doc__ with multiple overloads")
397 # find the first thing to look like a definition
398 prefix_re = re.compile("\s*(?:(\w+)[ \\t]+)?" + func_id + "\s*\(") # "foo(..." or "int foo(..."
399 match = prefix_re.search(func_doc) # Note: this and previous line may consume up to 35% of time
400 # parse the part that looks right
402 ret_hint = match.group(1)
403 params, ret_literal, doc_note = self.restore_by_docstring(func_doc[match.end():], class_name, deco, ret_hint)
404 spec = func_name + flatten(params)
405 return (spec, ret_literal, doc_note)
407 return (None, None, None)
410 def is_predefined_builtin(self, module_name, class_name, func_name):
411 return self.doing_builtins and module_name == BUILTIN_MOD_NAME and (
412 class_name, func_name) in PREDEFINED_BUILTIN_SIGS
415 def redo_function(self, out, p_func, p_name, indent, p_class=None, p_modname=None, classname=None, seen=None):
417 Restore function argument list as best we can.
418 @param out output function of a Buf
419 @param p_func function or method object
420 @param p_name function name as known to owner
421 @param indent indentation level
422 @param p_class the class that contains this function as a method
423 @param p_modname module name
424 @param seen {id(func): name} map of functions already seen in the same namespace;
425 id() because *some* functions are unhashable (eg _elementtree.Comment in py2.7)
427 action("redoing func %r of class %r", p_name, p_class)
429 other_func = seen.get(id(p_func), None)
430 if other_func and getattr(other_func, "__doc__", None) is getattr(p_func, "__doc__", None):
431 # _bisect.bisect == _bisect.bisect_right in py31, but docs differ
432 out(indent, p_name, " = ", seen[id(p_func)])
436 seen[id(p_func)] = p_name
438 if classname is None:
439 classname = p_class and p_class.__name__ or None
440 if p_class and hasattr(p_class, '__mro__'):
441 sip_generated = [base_t for base_t in p_class.__mro__ if 'sip.simplewrapper' in str(base_t)]
443 sip_generated = False
446 mod_class_method_tuple = (p_modname, classname, p_name)
450 action("redoing decos of func %r of class %r", p_name, p_class)
451 if self.doing_builtins and p_modname == BUILTIN_MOD_NAME:
452 deco = KNOWN_DECORATORS.get((classname, p_name), None)
454 deco_comment = " # known case"
455 elif p_class and p_name in p_class.__dict__:
456 # detect native methods declared with METH_CLASS flag
457 descriptor = p_class.__dict__[p_name]
458 if p_name != "__new__" and type(descriptor).__name__.startswith('classmethod'):
459 # 'classmethod_descriptor' in Python 2.x and 3.x, 'classmethod' in Jython
461 elif type(p_func).__name__.startswith('staticmethod'):
462 deco = "staticmethod"
463 if p_name == "__new__":
464 deco = "staticmethod"
465 deco_comment = " # known case of __new__"
467 action("redoing innards of func %r of class %r", p_name, p_class)
468 if deco and HAS_DECORATORS:
469 out(indent, "@", deco, deco_comment)
470 if inspect and inspect.isfunction(p_func):
471 out(indent, "def ", p_name, restore_by_inspect(p_func), ": # reliably restored by inspect", )
472 out_doc_attr(out, p_func, indent + 1, p_class)
473 elif self.is_predefined_builtin(*mod_class_method_tuple):
474 spec, sig_note = restore_predefined_builtin(classname, p_name)
475 out(indent, "def ", spec, ": # ", sig_note)
476 out_doc_attr(out, p_func, indent + 1, p_class)
477 elif sys.platform == 'cli' and is_clr_type(p_class):
478 spec, sig_note = restore_clr(p_name, p_class)
481 out(indent, "def ", spec, ": #", sig_note)
483 out(indent, "def ", spec, ":")
484 if not p_name in ['__gt__', '__ge__', '__lt__', '__le__', '__ne__', '__reduce_ex__', '__str__']:
485 out_doc_attr(out, p_func, indent + 1, p_class)
486 elif mod_class_method_tuple in PREDEFINED_MOD_CLASS_SIGS:
487 sig, ret_literal = PREDEFINED_MOD_CLASS_SIGS[mod_class_method_tuple]
489 ofwhat = "%s.%s.%s" % mod_class_method_tuple
491 ofwhat = "%s.%s" % (p_modname, p_name)
492 out(indent, "def ", p_name, sig, ": # known case of ", ofwhat)
493 out_doc_attr(out, p_func, indent + 1, p_class)
495 # __doc__ is our best source of arglist
496 sig_note = "real signature unknown"
498 is_init = (p_name == "__init__" and p_class is not None)
500 if is_init and hasattr(p_class, "__doc__"):
501 if hasattr(p_func, "__doc__"):
502 funcdoc = p_func.__doc__
503 if funcdoc == object.__init__.__doc__:
504 funcdoc = p_class.__doc__
505 elif hasattr(p_func, "__doc__"):
506 funcdoc = p_func.__doc__
508 action("parsing doc of func %r of class %r", p_name, p_class)
509 if isinstance(funcdoc, STR_TYPES):
510 (spec, ret_literal, more_notes) = self.parse_func_doc(funcdoc, p_name, p_name, classname, deco,
512 if spec is None and p_name == '__init__' and classname:
513 (spec, ret_literal, more_notes) = self.parse_func_doc(funcdoc, classname, p_name, classname, deco,
515 sig_restored = spec is not None
519 sig_note += more_notes
521 # use an allow-all declaration
524 first_param = propose_first_param(deco)
526 decl.append(first_param)
528 decl.append("**kwargs")
529 spec = p_name + "(" + ", ".join(decl) + ")"
530 out(indent, "def ", spec, ": # ", sig_note)
531 # to reduce size of stubs, don't output same docstring twice for class and its __init__ method
532 if not is_init or funcdoc != p_class.__doc__:
533 out_docstring(out, funcdoc, indent + 1)
535 if ret_literal and not is_init:
536 out(indent + 1, "return ", ret_literal)
538 out(indent + 1, "pass")
539 if deco and not HAS_DECORATORS:
540 out(indent, p_name, " = ", deco, "(", p_name, ")", deco_comment)
541 out(0, "") # empty line after each item
544 def redo_class(self, out, p_class, p_name, indent, p_modname=None, seen=None, inspect_dir=False):
546 Restores a class definition.
547 @param out output function of a relevant buf
548 @param p_class the class object
549 @param p_name class name as known to owner
550 @param indent indentation level
551 @param p_modname name of module
552 @param seen {class: name} map of classes already seen in the same namespace
554 action("redoing class %r of module %r", p_name, p_modname)
557 out(indent, p_name, " = ", seen[p_class])
561 seen[p_class] = p_name
562 bases = get_bases(p_class)
566 skip_qualifiers = [p_modname, BUILTIN_MOD_NAME, 'exceptions']
567 skip_qualifiers.extend(KNOWN_FAKE_REEXPORTERS.get(p_modname, ()))
568 bases_list = [] # what we'll render in the class decl
570 if [1 for (cls, mdl) in KNOWN_FAKE_BASES if cls == base and mdl != self.module]:
571 # our base is a wrapper and our module is not its defining module
572 skipped_bases.append(str(base))
574 # somehow import every base class
575 base_name = base.__name__
576 qual_module_name = qualifier_of(base, skip_qualifiers)
577 got_existing_import = False
579 if qual_module_name in self.used_imports:
580 import_list = self.used_imports[qual_module_name]
581 if base in import_list:
582 bases_list.append(base_name) # unqualified: already set to import
583 got_existing_import = True
584 if not got_existing_import:
585 mangled_qualifier = "__" + qual_module_name.replace('.', '_') # foo.bar -> __foo_bar
586 bases_list.append(mangled_qualifier + "." + base_name)
587 self.hidden_imports[qual_module_name] = mangled_qualifier
589 bases_list.append(base_name)
590 base_def = "(" + ", ".join(bases_list) + ")"
591 out(indent, "class ", p_name, base_def, ":",
592 skipped_bases and " # skipped bases: " + ", ".join(skipped_bases) or "")
593 out_doc_attr(out, p_class, indent + 1)
598 we_are_the_base_class = p_modname == BUILTIN_MOD_NAME and p_name == "object"
601 if hasattr(p_class, "__dict__") and not inspect_dir:
602 field_source = p_class.__dict__
603 field_keys = field_source.keys() # Jython 2.5.1 _codecs fail here
605 field_keys = dir(p_class) # this includes unwanted inherited methods, but no dict + inheritance is rare
608 for item_name in field_keys:
609 if item_name in ("__doc__", "__module__"):
610 if we_are_the_base_class:
611 item = "" # must be declared in base types
613 continue # in all other cases must be skipped
614 elif keyword.iskeyword(item_name): # for example, PyQt4 contains definitions of methods named 'exec'
618 item = getattr(p_class, item_name) # let getters do the magic
619 except AttributeError:
620 item = field_source[item_name] # have it raw
623 if is_callable(item) and not isinstance(item, type):
624 methods[item_name] = item
625 elif is_property(item):
626 properties[item_name] = item
628 others[item_name] = item
630 if we_are_the_base_class:
631 others["__dict__"] = {} # force-feed it, for __dict__ does not contain a reference to itself :)
632 # add fake __init__s to have the right sig
633 if p_class in FAKE_BUILTIN_INITS:
634 methods["__init__"] = self.fake_builtin_init
635 note("Faking init of %s", p_name)
636 elif '__init__' not in methods:
637 init_method = getattr(p_class, '__init__', None)
639 methods['__init__'] = init_method
643 for item_name in sorted_no_case(methods.keys()):
644 item = methods[item_name]
646 self.redo_function(out, item, item_name, indent + 1, p_class, p_modname, classname=p_name, seen=seen_funcs)
648 handle_error_func(item_name, out)
650 known_props = KNOWN_PROPS.get(p_modname, {})
651 a_setter = "lambda self, v: None"
652 a_deleter = "lambda self: None"
653 for item_name in sorted_no_case(properties.keys()):
654 item = properties[item_name]
655 prop_docstring = getattr(item, '__doc__', None)
656 prop_key = (p_name, item_name)
657 if prop_key in known_props:
658 prop_descr = known_props.get(prop_key, None)
659 if prop_descr is None:
660 continue # explicitly omitted
661 acc_line, getter_and_type = prop_descr
663 getter, prop_type = getter_and_type
665 getter, prop_type = None, None
666 out(indent + 1, item_name,
667 " = property(", format_accessors(acc_line, getter, a_setter, a_deleter), ")"
671 out(indent + 1, '"""', prop_docstring)
673 out(indent + 1, ':type: ', prop_type)
674 out(indent + 1, '"""')
676 out(indent + 1, '""":type: ', prop_type, '"""')
679 out(indent + 1, item_name, " = property(lambda self: object(), lambda self, v: None, lambda self: None) # default")
681 out(indent + 1, '"""', prop_docstring, '"""')
684 out(0, "") # empty line after the block
686 for item_name in sorted_no_case(others.keys()):
687 item = others[item_name]
688 self.fmt_value(out, item, indent + 1, prefix=item_name + " = ")
689 if p_name == "object":
690 out(indent + 1, "__module__ = ''")
692 out(0, "") # empty line after the block
694 if not methods and not properties and not others:
695 out(indent + 1, "pass")
699 def redo_simple_header(self, p_name):
700 """Puts boilerplate code on the top"""
701 out = self.header_buf.out # 1st class methods rule :)
702 out(0, "# encoding: %s" % OUT_ENCODING) # line 1
703 # NOTE: maybe encoding should be selectable
704 if hasattr(self.module, "__name__"):
705 self_name = self.module.__name__
706 if self_name != p_name:
707 mod_name = " calls itself " + self_name
711 mod_name = " does not know its name"
712 out(0, "# module ", p_name, mod_name) # line 2
714 BUILT_IN_HEADER = "(built-in)"
715 if self.mod_filename:
716 filename = self.mod_filename
717 elif p_name in sys.builtin_module_names:
718 filename = BUILT_IN_HEADER
720 filename = getattr(self.module, "__file__", BUILT_IN_HEADER)
722 out(0, "# from %s" % filename) # line 3
723 out(0, "# by generator %s" % VERSION) # line 4
724 if p_name == BUILTIN_MOD_NAME and version[0] == 2 and version[1] >= 6:
725 out(0, "from __future__ import print_function")
726 out_doc_attr(out, self.module, 0)
729 def redo_imports(self):
730 module_type = type(sys)
731 for item_name in self.module.__dict__.keys():
733 item = self.module.__dict__[item_name]
736 if type(item) is module_type: # not isinstance, py2.7 + PyQt4.QtCore on windows have a bug here
737 self.imported_modules[item_name] = item
738 self.add_import_header_if_needed()
739 ref_notice = getattr(item, "__file__", str(item))
740 if hasattr(item, "__name__"):
741 self.imports_buf.out(0, "import ", item.__name__, " as ", item_name, " # ", ref_notice)
743 self.imports_buf.out(0, item_name, " = None # ??? name unknown; ", ref_notice)
745 def add_import_header_if_needed(self):
746 if self.imports_buf.isEmpty():
747 self.imports_buf.out(0, "")
748 self.imports_buf.out(0, "# imports")
751 def redo(self, p_name, inspect_dir):
753 Restores module declarations.
754 Intended for built-in modules and thus does not handle import statements.
755 @param p_name name of module
757 action("redoing header of module %r %r", p_name, str(self.module))
759 if "pyqt" in p_name.lower(): # qt specific patch
760 self._initializeQApp()
762 self.redo_simple_header(p_name)
764 # find whatever other self.imported_modules the module knows; effectively these are imports
765 action("redoing imports of module %r %r", p_name, str(self.module))
771 action("redoing innards of module %r %r", p_name, str(self.module))
773 module_type = type(sys)
774 # group what we have into buckets
779 module_dict = self.module.__dict__
781 module_dict = dir(self.module)
782 for item_name in module_dict:
783 note("looking at %s", item_name)
785 "__dict__", "__doc__", "__module__", "__file__", "__name__", "__builtins__", "__package__"):
786 continue # handled otherwise
788 item = getattr(self.module, item_name) # let getters do the magic
789 except AttributeError:
790 if not item_name in self.module.__dict__: continue
791 item = self.module.__dict__[item_name] # have it raw
792 # check if it has percolated from an imported module
793 except NotImplementedError:
794 if not item_name in self.module.__dict__: continue
795 item = self.module.__dict__[item_name] # have it raw
797 # unless we're adamantly positive that the name was imported, we assume it is defined here
798 mod_name = None # module from which p_name might have been imported
799 # IronPython has non-trivial reexports in System module, but not in others:
800 skip_modname = sys.platform == "cli" and p_name != "System"
801 surely_not_imported_mods = KNOWN_FAKE_REEXPORTERS.get(p_name, ())
802 ## can't figure weirdness in some modules, assume no reexports:
803 #skip_modname = skip_modname or p_name in self.KNOWN_FAKE_REEXPORTERS
806 mod_name = getattr(item, '__module__', None)
809 # we assume that module foo.bar never imports foo; foo may import foo.bar. (see pygame and pygame.rect)
810 maybe_import_mod_name = mod_name or ""
811 import_is_from_top = len(p_name) > len(maybe_import_mod_name) and p_name.startswith(maybe_import_mod_name)
812 note("mod_name = %s, prospective = %s, from top = %s", mod_name, maybe_import_mod_name, import_is_from_top)
813 want_to_import = False
815 and mod_name != BUILTIN_MOD_NAME
816 and mod_name != p_name
817 and mod_name not in surely_not_imported_mods
818 and not import_is_from_top
820 # import looks valid, but maybe it's a .py file? we're certain not to import from .py
821 # e.g. this rules out _collections import collections and builtins import site.
823 imported = __import__(mod_name) # ok to repeat, Python caches for us
825 qualifiers = mod_name.split(".")[1:]
826 for qual in qualifiers:
827 imported = getattr(imported, qual, None)
830 imported_path = (getattr(imported, '__file__', False) or "").lower()
831 want_to_import = not (imported_path.endswith('.py') or imported_path.endswith('.pyc'))
832 note("path of %r is %r, want? %s", mod_name, imported_path, want_to_import)
834 want_to_import = False
835 # NOTE: if we fail to import, we define 'imported' names here lest we lose them at all
837 import_list = self.used_imports[mod_name]
838 if item_name not in import_list:
839 import_list.append(item_name)
840 if not want_to_import:
841 if isinstance(item, type) or type(item).__name__ == 'classobj':
842 classes[item_name] = item
843 elif is_callable(item): # some classes are callable, check them before functions
844 funcs[item_name] = item
845 elif isinstance(item, module_type):
846 continue # self.imported_modules handled above already
848 if isinstance(item, SIMPLEST_TYPES):
849 vars_simple[item_name] = item
851 vars_complex[item_name] = item
853 # sort and output every bucket
854 action("outputting innards of module %r %r", p_name, str(self.module))
856 omitted_names = OMIT_NAME_IN_MODULE.get(p_name, [])
858 out = self.functions_buf.out
859 prefix = "" # try to group variables by common prefix
860 PREFIX_LEN = 2 # default prefix length if we can't guess better
861 out(0, "# Variables with simple values")
862 for item_name in sorted_no_case(vars_simple.keys()):
863 if item_name in omitted_names:
864 out(0, "# definition of " + item_name + " omitted")
866 item = vars_simple[item_name]
868 if len(item_name) >= PREFIX_LEN:
869 prefix_pos = string.rfind(item_name, "_") # most prefixes end in an underscore
871 prefix_pos = PREFIX_LEN
872 beg = item_name[0:prefix_pos]
874 out(0, "") # space out from other prefix
879 replacement = REPLACE_MODULE_VALUES.get((p_name, item_name), None)
880 if replacement is not None:
881 out(0, item_name, " = ", replacement, " # real value of type ", str(type(item)), " replaced")
882 elif is_skipped_in_module(p_name, item_name):
884 out(0, item_name, " = ", self.invent_initializer(t_item), " # real value of type ", str(t_item),
887 self.fmt_value(out, item, 0, prefix=item_name + " = ")
888 self._defined[item_name] = True
889 out(0, "") # empty line after vars
892 out = self.functions_buf.out
893 out(0, "# functions")
896 for item_name in sorted_no_case(funcs.keys()):
897 if item_name in omitted_names:
898 out(0, "# definition of ", item_name, " omitted")
900 item = funcs[item_name]
902 self.redo_function(out, item, item_name, 0, p_modname=p_name, seen=seen_funcs)
904 handle_error_func(item_name, out)
906 self.functions_buf.out(0, "# no functions")
909 out = self.functions_buf.out
913 # sort classes so that inheritance order is preserved
914 cls_list = [] # items are (class_name, mro_tuple)
915 for cls_name in sorted_no_case(classes.keys()):
916 cls = classes[cls_name]
917 ins_index = len(cls_list)
918 for i in range(ins_index):
919 maybe_child_bases = cls_list[i][1]
920 if cls in maybe_child_bases:
921 ins_index = i # we could not go farther than current ins_index
922 break # ...and need not go fartehr than first known child
923 cls_list.insert(ins_index, (cls_name, get_mro(cls)))
924 for item_name in [cls_item[0] for cls_item in cls_list]:
925 if item_name in omitted_names:
926 out(0, "# definition of ", item_name, " omitted")
928 item = classes[item_name]
929 self.redo_class(out, item, item_name, 0, p_modname=p_name, seen=seen_classes, inspect_dir=inspect_dir)
930 self._defined[item_name] = True
931 out(0, "") # empty line after each item
933 if self.doing_builtins and p_name == BUILTIN_MOD_NAME and version[0] < 3:
934 # classobj still supported
936 self.classes_buf.out(0, txt)
938 if self.doing_builtins and p_name == BUILTIN_MOD_NAME:
939 txt = create_generator()
940 self.classes_buf.out(0, txt)
942 # Fake <type 'namedtuple'>
943 if version[0] >= 3 or (version[0] == 2 and version[1] >= 6):
944 namedtuple_text = create_named_tuple()
945 self.classes_buf.out(0, namedtuple_text)
948 self.classes_buf.out(0, "# no classes")
951 out = self.footer_buf.out
952 out(0, "# variables with complex values")
954 for item_name in sorted_no_case(vars_complex.keys()):
955 if item_name in omitted_names:
956 out(0, "# definition of " + item_name + " omitted")
958 item = vars_complex[item_name]
959 if str(type(item)) == "<type 'namespace#'>":
960 continue # this is an IronPython submodule, we mustn't generate a reference for it in the base module
961 replacement = REPLACE_MODULE_VALUES.get((p_name, item_name), None)
962 if replacement is not None:
963 out(0, item_name + " = " + replacement + " # real value of type " + str(type(item)) + " replaced")
964 elif is_skipped_in_module(p_name, item_name):
966 out(0, item_name + " = " + self.invent_initializer(t_item) + " # real value of type " + str(
967 t_item) + " skipped")
969 self.fmt_value(out, item, 0, prefix=item_name + " = ", as_name=item_name)
970 self._defined[item_name] = True
971 out(0, "") # empty line after each item
972 values_to_add = ADD_VALUE_IN_MODULE.get(p_name, None)
974 self.footer_buf.out(0, "# intermittent names")
975 for value in values_to_add:
976 self.footer_buf.out(0, value)
977 # imports: last, because previous parts could alter used_imports or hidden_imports
978 self.output_import_froms()
979 if self.imports_buf.isEmpty():
980 self.imports_buf.out(0, "# no imports")
981 self.imports_buf.out(0, "") # empty line after imports
983 def output_import_froms(self):
984 """Mention all imported names known within the module, wrapping as per PEP."""
985 out = self.imports_buf.out
986 if self.used_imports:
987 self.add_import_header_if_needed()
988 for mod_name in sorted_no_case(self.used_imports.keys()):
989 import_names = self.used_imports[mod_name]
991 self._defined[mod_name] = True
992 right_pos = 0 # tracks width of list to fold it at right margin
993 import_heading = "from % s import (" % mod_name
994 right_pos += len(import_heading)
995 names_pack = [import_heading]
997 import_names = list(import_names)
999 for n in import_names:
1000 self._defined[n] = True
1002 if right_pos + len_n >= 78:
1003 out(indent_level, *names_pack)
1004 names_pack = [n, ", "]
1005 if indent_level == 0:
1006 indent_level = 1 # all but first line is indented
1007 right_pos = self.indent_size + len_n + 2
1009 names_pack.append(n)
1010 names_pack.append(", ")
1011 right_pos += (len_n + 2)
1013 if indent_level == 0: # one line
1014 names_pack[0] = names_pack[0][:-1] # cut off lpar
1015 names_pack[-1] = "" # cut last comma
1016 else: # last line of multiline
1017 names_pack[-1] = ")" # last comma -> rpar
1018 out(indent_level, *names_pack)
1020 out(0, "") # empty line after group
1022 if self.hidden_imports:
1023 self.add_import_header_if_needed()
1024 for mod_name in sorted_no_case(self.hidden_imports.keys()):
1025 out(0, 'import ', mod_name, ' as ', self.hidden_imports[mod_name])
1026 out(0, "") # empty line after group