1 #!/usr/bin/env python 2 3 """ 4 Module abstractions. 5 6 Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012, 2013, 7 2014, 2015, 2016, 2017 Paul Boddie <paul@boddie.org.uk> 8 9 This program is free software; you can redistribute it and/or modify it under 10 the terms of the GNU General Public License as published by the Free Software 11 Foundation; either version 3 of the License, or (at your option) any later 12 version. 13 14 This program is distributed in the hope that it will be useful, but WITHOUT 15 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 16 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 17 details. 18 19 You should have received a copy of the GNU General Public License along with 20 this program. If not, see <http://www.gnu.org/licenses/>. 21 """ 22 23 from common import get_builtin_module, init_item, remove_items, CommonModule 24 from encoders import decode_modifier_term, decode_usage, encode_modifiers, encode_usage 25 from referencing import decode_reference, Reference 26 from results import ResolvedNameRef 27 import sys 28 29 class BasicModule(CommonModule): 30 31 "The basic module information." 32 33 def __init__(self, name, importer): 34 CommonModule.__init__(self, name, importer) 35 36 # Import details, primarily for cache output. 37 38 self.imports = set() 39 self.required = set() 40 self.deferred = [] 41 42 # Global name information. 43 44 self.objects = {} 45 self.special = {} 46 47 # Class relationships. 48 49 self.classes = {} 50 51 # Attributes. 52 53 self.class_attrs = {} 54 self.instance_attrs = {} 55 self.instance_attr_constants = {} 56 self.module_attrs = set() 57 58 # Names used in each namespace. 59 60 self.names_used = {} 61 62 # Function details. 63 64 self.function_parameters = {} 65 self.function_defaults = {} 66 self.function_locals = {} 67 self.scope_globals = {} 68 69 # Invocation details. 70 71 self.function_targets = {} 72 self.function_arguments = {} 73 74 # Exception handler details. 75 76 self.exception_namespaces = set() 77 78 # Attribute usage at module and function levels. 79 80 self.attr_usage = {} 81 self.name_initialisers = {} 82 83 # General attribute access expressions. 84 85 self.attr_accesses = {} 86 self.const_accesses = {} 87 88 # Attribute accessor definition details. 89 90 self.attr_accessors = {} 91 92 # Assignment details for accesses. 93 94 self.attr_access_modifiers = {} 95 96 # Name resolution details. 97 98 self.name_references = {} # references to globals 99 100 # Initialisation-related details. 101 102 self.initialised_names = {} 103 self.aliased_names = {} 104 105 def __repr__(self): 106 return "BasicModule(%r, %r)" % (self.name, self.importer) 107 108 # Derived information methods. 109 110 def propagate(self): 111 112 "Finalise and propagate module information." 113 114 self.propagate_attrs() 115 self.propagate_name_references() 116 self.propagate_attr_accesses() 117 self.propagate_constants() 118 119 def unpropagate(self): 120 121 """ 122 Retract information from the importer including information about this 123 module derived by the importer. 124 """ 125 126 del self.importer.all_module_attrs[self.name] 127 128 for name in self.classes.keys(): 129 del self.importer.classes[name] 130 del self.importer.all_class_attrs[name] 131 del self.importer.all_instance_attrs[name] 132 del self.importer.all_instance_attr_constants[name] 133 134 for name, bases in self.classes.items(): 135 for base in bases: 136 137 # Get the identity of the class from the reference. 138 139 base = base.get_origin() 140 141 try: 142 self.importer.subclasses[base].remove(name) 143 except (KeyError, ValueError): 144 pass 145 146 remove_items(self.importer.all_name_references, self.name_references) 147 remove_items(self.importer.all_initialised_names, self.initialised_names) 148 remove_items(self.importer.all_aliased_names, self.aliased_names) 149 remove_items(self.importer.all_attr_accesses, self.attr_accesses) 150 remove_items(self.importer.all_const_accesses, self.const_accesses) 151 remove_items(self.importer.all_attr_access_modifiers, self.attr_access_modifiers) 152 remove_items(self.importer.all_constants, self.constants) 153 remove_items(self.importer.all_constant_values, self.constant_values) 154 155 # Remove this module's objects from the importer. Objects are 156 # automatically propagated when defined. 157 158 ref = self.importer.objects.get(self.name) 159 if ref and ref.has_kind("<module>"): 160 del self.importer.objects[self.name] 161 162 for name, ref in self.objects.items(): 163 if not ref.has_kind("<module>"): 164 del self.importer.objects[name] 165 166 def collect(self): 167 168 "Collect removed objects." 169 170 for name, ref in self.objects.items(): 171 if not self.importer.objects.has_key(ref.get_origin()) and self.importer.objects.has_key(name): 172 del self.importer.objects[name] 173 174 def propagate_attrs(self): 175 176 "Derive attributes from the class and module member details." 177 178 # Initialise class attribute records for all classes. 179 180 for name in self.classes.keys(): 181 self.importer.all_class_attrs[name] = self.class_attrs[name] = {} 182 183 # Separate the objects into module and class attributes. 184 185 for name in self.objects.keys(): 186 if "." in name: 187 parent, attrname = name.rsplit(".", 1) 188 if self.classes.has_key(parent): 189 self.class_attrs[parent][attrname] = name 190 elif parent == self.name: 191 self.module_attrs.add(attrname) 192 193 # Propagate the module attributes. 194 195 self.importer.all_module_attrs[self.name] = self.module_attrs 196 197 def propagate_name_references(self): 198 199 "Propagate name references for the module." 200 201 self.importer.all_initialised_names.update(self.initialised_names) 202 self.importer.all_aliased_names.update(self.aliased_names) 203 204 def propagate_attr_accesses(self): 205 206 "Propagate attribute accesses for the module." 207 208 self.importer.all_attr_accesses.update(self.attr_accesses) 209 self.importer.all_const_accesses.update(self.const_accesses) 210 self.importer.all_attr_access_modifiers.update(self.attr_access_modifiers) 211 212 def propagate_constants(self): 213 214 "Propagate constant values and aliases for the module." 215 216 self.importer.all_constants.update(self.constants) 217 self.importer.all_constant_values.update(self.constant_values) 218 219 for name in self.classes.keys(): 220 self.importer.all_instance_attrs[name] = self.instance_attrs.get(name) or {} 221 self.importer.all_instance_attr_constants[name] = self.instance_attr_constants.get(name) or {} 222 223 def set_object(self, name, value=None): 224 225 "Set an object with the given 'name' and the given 'value'." 226 227 # Decode any string value, with a new reference being returned even 228 # given a provided reference. 229 230 ref = decode_reference(value, name) 231 self.add_deferred(ref) 232 self._set_object(name, ref) 233 234 def _set_object(self, name, ref): 235 236 # Determine how the object properties will be defined. 237 238 multiple = self.objects.has_key(name) and self.objects[name] != ref 239 self.importer.objects[name] = self.objects[name] = multiple and ref.as_var() or ref 240 241 def queue_module(self, name, required=False): 242 243 """ 244 Queue the module with the given 'name'. If 'required' is true (it is 245 false by default), indicate that the module is required in the final 246 program. 247 """ 248 249 self.importer.queue_module(name, self, required) 250 if required: 251 self.required.add(name) 252 self.imports.add(name) 253 254 class InspectionNaming: 255 256 "Name operations related to inspection." 257 258 # Module-relative naming. 259 260 def is_global(self, name): 261 262 """ 263 Return whether 'name' is registered as a global in the current 264 namespace. 265 """ 266 267 path = self.get_namespace_path() 268 return name in self.scope_globals.get(path, []) 269 270 def get_global(self, name): 271 272 """ 273 Get the global of the given 'name' from this module, returning a 274 reference incorporating the original definition details. 275 """ 276 277 path = self.get_global_path(name) 278 return self.objects.get(path) 279 280 # Name definition discovery. 281 282 def get_global_or_builtin(self, name): 283 284 """ 285 Return a reference for the given 'name' found in this module or in the 286 __builtins__. 287 """ 288 289 return self.get_global(name) or self.get_builtin(name) 290 291 def get_builtin(self, name): 292 293 "Return a reference to the built-in with the given 'name'." 294 295 self.queue_module("__builtins__") 296 ref = Reference("<depends>", "__builtins__.%s" % name) 297 self.deferred.append(ref) 298 return ref 299 300 def get_builtin_class(self, name): 301 302 "Return a reference to the actual object providing 'name'." 303 304 # NOTE: This makes assumptions about the __builtins__ structure. 305 306 modname = get_builtin_module(name) 307 module_name = "__builtins__.%s" % modname 308 309 if self.name != module_name: 310 self.queue_module(module_name, True) 311 312 return Reference("<class>", "__builtins__.%s.%s" % (modname, name)) 313 314 def get_object(self, path, defer=True): 315 316 """ 317 Get the details of an object with the given 'path'. Where the object 318 cannot be resolved, an unresolved reference is returned if 'defer' is 319 set to a true value (the default). Otherwise, None is returned. 320 """ 321 322 if self.objects.has_key(path): 323 return self.objects[path] 324 elif defer: 325 ref = Reference("<depends>", path) 326 self.deferred.append(ref) 327 return ref 328 else: 329 return None 330 331 def import_name_from_module(self, name, module_name): 332 333 "Import 'name' from the module having the given 'module_name'." 334 335 if module_name != self.name: 336 self.queue_module(module_name) 337 ref = Reference("<depends>", "%s.%s" % (module_name, name)) 338 self.deferred.append(ref) 339 return ref 340 341 def add_deferred(self, ref): 342 343 "Record 'ref' as a deferred reference." 344 345 if ref.has_kind("<depends>"): 346 self.deferred.append(ref) 347 348 class CachedModule(BasicModule): 349 350 "A cached module." 351 352 def __repr__(self): 353 return "CachedModule(%r, %r)" % (self.name, self.importer) 354 355 def set_object(self, name, value=None): 356 357 "Set an object with the given 'name' and the given 'value'." 358 359 # Decode any string value, with a new reference being returned even 360 # given a provided reference. 361 362 ref = decode_reference(value, name) 363 self._set_object(name, ref) 364 365 def to_cache(self, filename): 366 367 "Not actually writing the module back to 'filename'." 368 369 pass 370 371 def from_cache(self, filename): 372 373 """ 374 Read a module's details from the file with the given 'filename' as 375 described in the to_cache method of InspectedModule. 376 """ 377 378 f = open(filename) 379 try: 380 self.filename = f.readline().rstrip() 381 382 f.readline() # (empty line) 383 384 self._get_imports(f) 385 self._get_deferred(f) 386 self._get_special(f) 387 self._get_members(f) 388 self._get_class_relationships(f) 389 self._get_instance_attrs(f) 390 self._get_instance_attr_constants(f) 391 self.from_lines(f, self.names_used) # "names used:" 392 self._get_name_references(f) 393 self._get_initialised_names(f) 394 self._get_aliased_names(f) 395 self._get_function_parameters(f) 396 self._get_function_defaults(f) 397 self._get_function_locals(f) 398 self.from_lines(f, self.scope_globals) # "scope globals:" 399 self._get_function_targets(f) 400 self._get_function_arguments(f) 401 self._get_attribute_usage(f) 402 self._get_attr_accesses(f) 403 self._get_const_accesses(f) 404 self._get_attr_accessors(f) 405 self._get_attr_access_modifiers(f) 406 self._get_constant_literals(f) 407 self._get_constant_values(f) 408 self._get_exception_namespaces(f) 409 410 finally: 411 f.close() 412 413 def complete(self): 414 self.propagate() 415 416 def _get_imports(self, f): 417 f.readline() # "imports:" 418 line = f.readline().strip() 419 self.required = line != "{}" and set(line.split(", ")) or set() 420 line = f.readline().strip() 421 self.imports = line != "{}" and set(line.split(", ")) or set() 422 f.readline() 423 424 for name in self.required: 425 self.queue_module(name, True) 426 for name in self.imports: 427 self.queue_module(name) 428 429 def _get_deferred(self, f): 430 f.readline() # "deferred:" 431 line = f.readline().rstrip() 432 self.deferred = map(decode_reference, line.split(", ")) 433 f.readline() 434 435 def _get_special(self, f): 436 f.readline() # "special:" 437 line = f.readline().rstrip() 438 while line: 439 name, ref, paths = self._get_fields(line, 3) 440 self.special[name] = decode_reference(ref), paths.split(", ") 441 line = f.readline().rstrip() 442 443 def _get_members(self, f): 444 f.readline() # "members:" 445 line = f.readline().rstrip() 446 while line: 447 name, ref = line.split(" ", 1) 448 self.set_object(name, ref) 449 line = f.readline().rstrip() 450 451 def _get_class_relationships(self, f): 452 f.readline() # "class relationships:" 453 line = f.readline().rstrip() 454 while line: 455 name, value = self._get_fields(line) 456 values = value and value.split(", ") or [] 457 self.importer.classes[name] = self.classes[name] = map(decode_reference, values) 458 self.importer.subclasses[name] = set() 459 line = f.readline().rstrip() 460 461 def _get_instance_attrs(self, f): 462 f.readline() # "instance attributes:" 463 line = f.readline().rstrip() 464 while line: 465 name, value = self._get_fields(line) 466 self.importer.all_instance_attrs[name] = self.instance_attrs[name] = set(value and value.split(", ") or []) 467 line = f.readline().rstrip() 468 469 def _get_instance_attr_constants(self, f): 470 f.readline() # "instance attribute constants:" 471 line = f.readline().rstrip() 472 while line: 473 name, attrname, ref = self._get_fields(line, 3) 474 init_item(self.instance_attr_constants, name, dict) 475 self.instance_attr_constants[name][attrname] = decode_reference(ref) 476 line = f.readline().rstrip() 477 478 def _get_name_references(self, f): 479 f.readline() # "name references:" 480 line = f.readline().rstrip() 481 while line: 482 name, ref = self._get_fields(line) 483 self.importer.all_name_references[name] = self.name_references[name] = decode_reference(ref) 484 line = f.readline().rstrip() 485 486 def _get_initialised_names(self, f): 487 f.readline() # "initialised names:" 488 line = f.readline().rstrip() 489 while line: 490 name, version, value = self._get_fields(line, 3) 491 init_item(self.initialised_names, name, dict) 492 self.initialised_names[name][int(version)] = decode_reference(value) 493 line = f.readline().rstrip() 494 495 def _get_aliased_names(self, f): 496 f.readline() # "aliased names:" 497 line = f.readline().rstrip() 498 while line: 499 name, version, original_name, attrnames, number = self._get_fields(line, 5) 500 init_item(self.aliased_names, name, dict) 501 if number == "{}": number = None 502 else: number = int(number) 503 self.aliased_names[name][int(version)] = (original_name, attrnames != "{}" and attrnames or None, number) 504 line = f.readline().rstrip() 505 506 def _get_function_parameters(self, f): 507 f.readline() # "function parameters:" 508 line = f.readline().rstrip() 509 while line: 510 function, names = self._get_fields(line) 511 self.importer.function_parameters[function] = \ 512 self.function_parameters[function] = names != "{}" and names.split(", ") or [] 513 line = f.readline().rstrip() 514 515 def _get_function_defaults(self, f): 516 f.readline() # "function default parameters:" 517 line = f.readline().rstrip() 518 while line: 519 function, defaults = self._get_fields(line) 520 self.importer.function_defaults[function] = \ 521 self.function_defaults[function] = l = [] 522 if defaults != "{}": 523 for value in defaults.split(", "): 524 name, default = value.split("=") 525 default = decode_reference(default) 526 l.append((name, default)) 527 line = f.readline().rstrip() 528 529 def _get_function_locals(self, f): 530 f.readline() # "function locals:" 531 line = f.readline().rstrip() 532 while line: 533 function, name, value = self._get_fields(line, 3) 534 init_item(self.function_locals, function, dict) 535 init_item(self.importer.function_locals, function, dict) 536 if name != "{}": 537 self.importer.function_locals[function][name] = \ 538 self.function_locals[function][name] = decode_reference(value) 539 line = f.readline().rstrip() 540 541 def _get_function_targets(self, f): 542 f.readline() # "function targets:" 543 line = f.readline().rstrip() 544 while line: 545 function, n = self._get_fields(line) 546 self.importer.function_targets[function] = \ 547 self.function_targets[function] = int(n) 548 line = f.readline().rstrip() 549 550 def _get_function_arguments(self, f): 551 f.readline() # "function arguments:" 552 line = f.readline().rstrip() 553 while line: 554 function, n = self._get_fields(line) 555 self.importer.function_arguments[function] = \ 556 self.function_arguments[function] = int(n) 557 line = f.readline().rstrip() 558 559 def _get_attribute_usage(self, f): 560 f.readline() # "attribute usage:" 561 line = f.readline().rstrip() 562 while line: 563 unit, value = self._get_fields(line) 564 init_item(self.attr_usage, unit, dict) 565 self.usage_from_cache(value, self.attr_usage[unit]) 566 line = f.readline().rstrip() 567 568 def _get_attr_accesses(self, f): 569 f.readline() # "attribute accesses:" 570 line = f.readline().rstrip() 571 while line: 572 name, value = self._get_fields(line) 573 self.attr_accesses[name] = set(value.split(", ")) 574 line = f.readline().rstrip() 575 576 def _get_const_accesses(self, f): 577 f.readline() # "constant accesses:" 578 line = f.readline().rstrip() 579 while line: 580 name, original_name, attrnames, objpath, ref, remaining = self._get_fields(line, 6) 581 if attrnames == "{}": attrnames = None 582 init_item(self.const_accesses, name, dict) 583 self.const_accesses[name][(original_name, attrnames)] = (objpath, decode_reference(ref), remaining != "{}" and remaining or "") 584 line = f.readline().rstrip() 585 586 def _get_attr_accessors(self, f): 587 f.readline() # "attribute access usage:" 588 line = f.readline().rstrip() 589 while line: 590 objpath, name, attrname, value = self._get_fields(line, 4) 591 if attrname == "{}": attrname = None 592 access = name, attrname 593 init_item(self.attr_accessors, objpath, dict) 594 init_item(self.attr_accessors[objpath], access, list) 595 positions = map(int, value.split(", ")) 596 self.attr_accessors[objpath][access].append(positions) 597 line = f.readline().rstrip() 598 599 def _get_attr_access_modifiers(self, f): 600 f.readline() # "attribute access modifiers:" 601 line = f.readline().rstrip() 602 while line: 603 objpath, name, attrnames, value = self._get_fields(line, 4) 604 if name == "{}": name = None 605 if attrnames == "{}": attrnames = None 606 access = name, attrnames 607 init_item(self.attr_access_modifiers, objpath, dict) 608 init_item(self.attr_access_modifiers[objpath], access, list) 609 modifiers = [decode_modifier_term(s) for s in value] 610 self.attr_access_modifiers[objpath][access] = modifiers 611 line = f.readline().rstrip() 612 613 def _get_constant_literals(self, f): 614 f.readline() # "constant literals:" 615 line = f.readline().rstrip() 616 last_path = None 617 n = None 618 while line: 619 path, value_type, encoding, value = self._get_fields(line, 4) 620 if path != last_path: 621 n = 0 622 last_path = path 623 else: 624 n += 1 625 init_item(self.constants, path, dict) 626 value = eval(value) 627 encoding = encoding != "{}" and encoding or None 628 self.constants[path][(value, value_type, encoding)] = n 629 line = f.readline().rstrip() 630 631 def _get_constant_values(self, f): 632 f.readline() # "constant values:" 633 line = f.readline().rstrip() 634 while line: 635 name, value_type, encoding, value = self._get_fields(line, 4) 636 value = eval(value) 637 encoding = encoding != "{}" and encoding or None 638 self.constant_values[name] = value, value_type, encoding 639 line = f.readline().rstrip() 640 641 def _get_exception_namespaces(self, f): 642 f.readline() # "exception namespaces:" 643 value = f.readline().rstrip() 644 self.exception_namespaces = value and set(value.split(", ")) or set() 645 f.readline() 646 647 # Generic parsing methods. 648 649 def from_lines(self, f, d): 650 651 "Read lines from 'f', populating 'd'." 652 653 f.readline() # section heading 654 line = f.readline().rstrip() 655 while line: 656 name, value = self._get_fields(line) 657 d[name] = set(value and value.split(", ") or []) 658 line = f.readline().rstrip() 659 660 def usage_from_cache(self, value, mapping): 661 662 """ 663 Interpret the given 'value' containing name and usage information, 664 storing the information in the given 'mapping'. 665 """ 666 667 local, usage = self._get_fields(value) 668 init_item(mapping, local, list) 669 self._usage_from_cache(mapping[local], usage) 670 671 def _usage_from_cache(self, d, usage): 672 673 # Interpret descriptions of each version of the name. 674 675 all_usages = set() 676 for attrnames in usage.split("; "): 677 if attrnames == "{}": 678 all_attrnames = () 679 else: 680 all_attrnames = decode_usage(attrnames) 681 all_usages.add(all_attrnames) 682 683 d.append(all_usages) 684 685 def _get_fields(self, s, n=2): 686 result = s.split(" ", n-1) 687 if len(result) == n: 688 return result 689 else: 690 return tuple(result) + tuple([""] * (n - len(result))) 691 692 class CacheWritingModule: 693 694 """ 695 A mix-in providing cache-writing support, to be combined with BasicModule. 696 """ 697 698 def to_cache(self, filename): 699 700 """ 701 Write a cached representation of the inspected module to the file having 702 the given 'filename'. 703 """ 704 705 f = open(filename, "w") 706 try: 707 print >>f, self.filename 708 709 print >>f 710 print >>f, "imports:" 711 required = list(self.required) 712 required.sort() 713 print >>f, required and ", ".join(required) or "{}" 714 imports = list(self.imports) 715 imports.sort() 716 print >>f, imports and ", ".join(imports) or "{}" 717 718 print >>f 719 print >>f, "deferred:" 720 deferred = map(str, set(self.deferred)) 721 deferred.sort() 722 print >>f, ", ".join(deferred) 723 724 print >>f 725 print >>f, "special:" 726 names = self.special.keys() 727 names.sort() 728 for name in names: 729 ref, paths = self.special[name] 730 print >>f, name, ref, ", ".join(paths) 731 732 print >>f 733 print >>f, "members:" 734 objects = self.objects.keys() 735 objects.sort() 736 for name in objects: 737 print >>f, name, self.objects[name] 738 739 print >>f 740 print >>f, "class relationships:" 741 classes = self.classes.keys() 742 classes.sort() 743 for class_ in classes: 744 bases = self.classes[class_] 745 if bases: 746 print >>f, class_, ", ".join(map(str, bases)) 747 else: 748 print >>f, class_ 749 750 self.to_lines(f, "instance attributes:", self.instance_attrs) 751 752 print >>f 753 print >>f, "instance attribute constants:" 754 classes = self.instance_attr_constants.items() 755 classes.sort() 756 for name, attrs in classes: 757 attrs = attrs.items() 758 attrs.sort() 759 for attrname, ref in attrs: 760 print >>f, name, attrname, ref 761 762 self.to_lines(f, "names used:", self.names_used) 763 764 print >>f 765 print >>f, "name references:" 766 refs = self.name_references.items() 767 refs.sort() 768 for name, ref in refs: 769 print >>f, name, ref 770 771 print >>f 772 print >>f, "initialised names:" 773 assignments = self.initialised_names.items() 774 assignments.sort() 775 for name, refs in assignments: 776 versions = refs.items() 777 versions.sort() 778 for version, ref in versions: 779 print >>f, name, version, ref 780 781 print >>f 782 print >>f, "aliased names:" 783 assignments = self.aliased_names.items() 784 assignments.sort() 785 for name, aliases in assignments: 786 versions = aliases.items() 787 versions.sort() 788 for version, alias in versions: 789 original_name, attrnames, number = alias 790 print >>f, name, version, original_name, attrnames or "{}", number is None and "{}" or number 791 792 print >>f 793 print >>f, "function parameters:" 794 functions = self.function_parameters.keys() 795 functions.sort() 796 for function in functions: 797 parameters = self.function_parameters[function] 798 if parameters: 799 print >>f, function, ", ".join(parameters) 800 else: 801 print >>f, function, "{}" 802 803 print >>f 804 print >>f, "function default parameters:" 805 functions = self.function_defaults.keys() 806 functions.sort() 807 for function in functions: 808 parameters = self.function_defaults[function] 809 if parameters: 810 print >>f, function, ", ".join([("%s=%s" % (name, default)) for (name, default) in parameters]) 811 else: 812 print >>f, function, "{}" 813 814 print >>f 815 print >>f, "function locals:" 816 functions = self.function_locals.keys() 817 functions.sort() 818 for function in functions: 819 names = self.function_locals[function].items() 820 if names: 821 names.sort() 822 for name, value in names: 823 print >>f, function, name, value 824 else: 825 print >>f, function, "{}" 826 827 self.to_lines(f, "scope globals:", self.scope_globals) 828 829 print >>f 830 print >>f, "function targets:" 831 functions = self.function_targets.keys() 832 functions.sort() 833 for function in functions: 834 print >>f, function, self.function_targets[function] 835 836 print >>f 837 print >>f, "function arguments:" 838 functions = self.function_arguments.keys() 839 functions.sort() 840 for function in functions: 841 print >>f, function, self.function_arguments[function] 842 843 print >>f 844 print >>f, "attribute usage:" 845 units = self.attr_usage.keys() 846 units.sort() 847 for unit in units: 848 d = self.attr_usage[unit] 849 self.usage_to_cache(d, f, unit) 850 851 print >>f 852 print >>f, "attribute accesses:" 853 paths = self.attr_accesses.keys() 854 paths.sort() 855 for path in paths: 856 accesses = list(self.attr_accesses[path]) 857 accesses.sort() 858 print >>f, path, ", ".join(accesses) 859 860 print >>f 861 print >>f, "constant accesses:" 862 paths = self.const_accesses.keys() 863 paths.sort() 864 for path in paths: 865 accesses = self.const_accesses[path].items() 866 accesses.sort() 867 for (original_name, attrnames), (objpath, ref, remaining_attrnames) in accesses: 868 print >>f, path, original_name, attrnames, objpath, ref, remaining_attrnames or "{}" 869 870 print >>f 871 print >>f, "attribute access usage:" 872 paths = self.attr_accessors.keys() 873 paths.sort() 874 for path in paths: 875 all_accesses = self.attr_accessors[path].items() 876 all_accesses.sort() 877 for (name, attrname), accesses in all_accesses: 878 for positions in accesses: 879 positions = map(str, positions) 880 print >>f, path, name, attrname or "{}", ", ".join(positions) 881 882 print >>f 883 print >>f, "attribute access modifiers:" 884 paths = self.attr_access_modifiers.keys() 885 paths.sort() 886 for path in paths: 887 all_accesses = self.attr_access_modifiers[path].items() 888 all_accesses.sort() 889 for (name, attrnames), modifiers in all_accesses: 890 print >>f, path, name or "{}", attrnames or "{}", encode_modifiers(modifiers) 891 892 print >>f 893 print >>f, "constant literals:" 894 paths = self.constants.keys() 895 paths.sort() 896 for path in paths: 897 constants = [] 898 for (value, value_type, encoding), n in self.constants[path].items(): 899 constants.append((n, value_type, encoding, value)) 900 constants.sort() 901 for n, value_type, encoding, value in constants: 902 print >>f, path, value_type, encoding or "{}", repr(value) 903 904 print >>f 905 print >>f, "constant values:" 906 names = self.constant_values.keys() 907 names.sort() 908 for name in names: 909 value, value_type, encoding = self.constant_values[name] 910 print >>f, name, value_type, encoding or "{}", repr(value) 911 912 print >>f 913 print >>f, "exception namespaces:" 914 paths = list(self.exception_namespaces) 915 paths.sort() 916 print >>f, ", ".join(paths) 917 918 finally: 919 f.close() 920 921 def to_lines(self, f, heading, d): 922 923 "Write lines to 'f' with the given 'heading', using 'd'." 924 925 print >>f 926 print >>f, heading 927 keys = d.keys() 928 keys.sort() 929 for key in keys: 930 attrs = list(d[key]) 931 if attrs: 932 attrs.sort() 933 print >>f, key, ", ".join(attrs) 934 935 def usage_to_cache(self, details, f, prefix): 936 937 "Write the given namespace usage details to the cache." 938 939 names = list(details.keys()) 940 if names: 941 names.sort() 942 for name in names: 943 if details[name]: 944 945 # Produce descriptions for each version of the name. 946 947 for version in details[name]: 948 all_usages = [] 949 for usage in version: 950 all_usages.append(encode_usage(usage)) 951 952 print >>f, "%s %s %s" % (prefix, name, "; ".join(all_usages)) 953 954 # vim: tabstop=4 expandtab shiftwidth=4