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_class, get_builtin_module, init_item, \ 24 remove_items, CommonModule 25 from encoders import decode_modifiers, decode_usage, encode_modifiers, encode_usage 26 from referencing import decode_reference, Reference 27 from results import ResolvedNameRef 28 import sys 29 30 class BasicModule(CommonModule): 31 32 "The basic module information." 33 34 def __init__(self, name, importer): 35 CommonModule.__init__(self, name, importer) 36 37 # Import details, primarily for cache output. 38 39 self.imports = set() 40 self.required = set() 41 self.deferred = [] 42 43 # Global name information. 44 45 self.objects = {} 46 self.special = {} 47 48 # Class relationships. 49 50 self.classes = {} 51 52 # Attributes. 53 54 self.class_attrs = {} 55 self.instance_attrs = {} 56 self.instance_attr_constants = {} 57 self.module_attrs = set() 58 59 # Names used in each namespace. 60 61 self.names_used = {} 62 63 # Function details. 64 65 self.function_parameters = {} 66 self.function_defaults = {} 67 self.function_locals = {} 68 self.scope_globals = {} 69 70 # Exception handler details. 71 72 self.exception_namespaces = set() 73 74 # Return value details. 75 76 self.return_values = {} 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 objpath = get_builtin_class(name) 305 module_name = get_builtin_module(name) 306 307 if self.name != module_name: 308 self.queue_module(module_name, True) 309 310 return Reference("<class>", objpath) 311 312 def get_object(self, path, defer=True): 313 314 """ 315 Get the details of an object with the given 'path'. Where the object 316 cannot be resolved, an unresolved reference is returned if 'defer' is 317 set to a true value (the default). Otherwise, None is returned. 318 """ 319 320 if self.objects.has_key(path): 321 return self.objects[path] 322 elif defer: 323 ref = Reference("<depends>", path) 324 self.deferred.append(ref) 325 return ref 326 else: 327 return None 328 329 def import_name_from_module(self, name, module_name): 330 331 "Import 'name' from the module having the given 'module_name'." 332 333 if module_name != self.name: 334 self.queue_module(module_name) 335 ref = Reference("<depends>", "%s.%s" % (module_name, name)) 336 self.deferred.append(ref) 337 return ref 338 339 def add_deferred(self, ref): 340 341 "Record 'ref' as a deferred reference." 342 343 if ref.has_kind("<depends>"): 344 self.deferred.append(ref) 345 346 class CachedModule(BasicModule): 347 348 "A cached module." 349 350 def __repr__(self): 351 return "CachedModule(%r, %r)" % (self.name, self.importer) 352 353 def set_object(self, name, value=None): 354 355 "Set an object with the given 'name' and the given 'value'." 356 357 # Decode any string value, with a new reference being returned even 358 # given a provided reference. 359 360 ref = decode_reference(value, name) 361 self._set_object(name, ref) 362 363 def to_cache(self, filename): 364 365 "Not actually writing the module back to 'filename'." 366 367 pass 368 369 def from_cache(self, filename): 370 371 """ 372 Read a module's details from the file with the given 'filename' as 373 described in the to_cache method of InspectedModule. 374 """ 375 376 f = open(filename) 377 try: 378 self.filename = f.readline().rstrip() 379 380 f.readline() # (empty line) 381 382 self._get_imports(f) 383 self._get_deferred(f) 384 self._get_special(f) 385 self._get_members(f) 386 self._get_class_relationships(f) 387 self._get_instance_attrs(f) 388 self._get_instance_attr_constants(f) 389 self.from_lines(f, self.names_used) # "names used:" 390 self._get_name_references(f) 391 self._get_initialised_names(f) 392 self._get_aliased_names(f) 393 self._get_function_parameters(f) 394 self._get_function_defaults(f) 395 self._get_function_locals(f) 396 self.from_lines(f, self.scope_globals) # "scope globals:" 397 self._get_attribute_usage(f) 398 self._get_attr_accesses(f) 399 self._get_const_accesses(f) 400 self._get_attr_accessors(f) 401 self._get_attr_access_modifiers(f) 402 self._get_constant_literals(f) 403 self._get_constant_values(f) 404 self._get_exception_namespaces(f) 405 406 finally: 407 f.close() 408 409 def complete(self): 410 self.propagate() 411 412 def _get_imports(self, f): 413 f.readline() # "imports:" 414 line = f.readline().strip() 415 self.required = line != "{}" and set(line.split(", ")) or set() 416 line = f.readline().strip() 417 self.imports = line != "{}" and set(line.split(", ")) or set() 418 f.readline() 419 420 for name in self.required: 421 self.queue_module(name, True) 422 for name in self.imports: 423 self.queue_module(name) 424 425 def _get_deferred(self, f): 426 f.readline() # "deferred:" 427 line = f.readline().rstrip() 428 self.deferred = map(decode_reference, line.split(", ")) 429 f.readline() 430 431 def _get_special(self, f): 432 f.readline() # "special:" 433 line = f.readline().rstrip() 434 while line: 435 name, ref, paths = self._get_fields(line, 3) 436 self.special[name] = decode_reference(ref), paths.split(", ") 437 line = f.readline().rstrip() 438 439 def _get_members(self, f): 440 f.readline() # "members:" 441 line = f.readline().rstrip() 442 while line: 443 name, ref = line.split(" ", 1) 444 self.set_object(name, ref) 445 line = f.readline().rstrip() 446 447 def _get_class_relationships(self, f): 448 f.readline() # "class relationships:" 449 line = f.readline().rstrip() 450 while line: 451 name, value = self._get_fields(line) 452 values = value and value.split(", ") or [] 453 self.importer.classes[name] = self.classes[name] = map(decode_reference, values) 454 self.importer.subclasses[name] = set() 455 line = f.readline().rstrip() 456 457 def _get_instance_attrs(self, f): 458 f.readline() # "instance attributes:" 459 line = f.readline().rstrip() 460 while line: 461 name, value = self._get_fields(line) 462 self.importer.all_instance_attrs[name] = self.instance_attrs[name] = set(value and value.split(", ") or []) 463 line = f.readline().rstrip() 464 465 def _get_instance_attr_constants(self, f): 466 f.readline() # "instance attribute constants:" 467 line = f.readline().rstrip() 468 while line: 469 name, attrname, ref = self._get_fields(line, 3) 470 init_item(self.instance_attr_constants, name, dict) 471 self.instance_attr_constants[name][attrname] = decode_reference(ref) 472 line = f.readline().rstrip() 473 474 def _get_name_references(self, f): 475 f.readline() # "name references:" 476 line = f.readline().rstrip() 477 while line: 478 name, ref = self._get_fields(line) 479 self.importer.all_name_references[name] = self.name_references[name] = decode_reference(ref) 480 line = f.readline().rstrip() 481 482 def _get_initialised_names(self, f): 483 f.readline() # "initialised names:" 484 line = f.readline().rstrip() 485 while line: 486 path, name, version, value = self._get_fields(line, 4) 487 init_item(self.initialised_names, (path, name), dict) 488 self.initialised_names[(path, name)][int(version)] = decode_reference(value) 489 line = f.readline().rstrip() 490 491 def _get_aliased_names(self, f): 492 f.readline() # "aliased names:" 493 line = f.readline().rstrip() 494 while line: 495 path, name, version, original_path, original_name, attrnames, number = self._get_fields(line, 7) 496 init_item(self.aliased_names, (path, name), dict) 497 if number == "{}": number = None 498 else: number = int(number) 499 self.aliased_names[(path, name)][int(version)] = (original_path, original_name, attrnames != "{}" and attrnames or None, number) 500 line = f.readline().rstrip() 501 502 def _get_function_parameters(self, f): 503 f.readline() # "function parameters:" 504 line = f.readline().rstrip() 505 while line: 506 function, names = self._get_fields(line) 507 self.importer.function_parameters[function] = \ 508 self.function_parameters[function] = names != "{}" and names.split(", ") or [] 509 line = f.readline().rstrip() 510 511 def _get_function_defaults(self, f): 512 f.readline() # "function default parameters:" 513 line = f.readline().rstrip() 514 while line: 515 function, defaults = self._get_fields(line) 516 self.importer.function_defaults[function] = \ 517 self.function_defaults[function] = l = [] 518 if defaults != "{}": 519 for value in defaults.split(", "): 520 name, default = value.split("=") 521 default = decode_reference(default) 522 l.append((name, default)) 523 line = f.readline().rstrip() 524 525 def _get_function_locals(self, f): 526 f.readline() # "function locals:" 527 line = f.readline().rstrip() 528 while line: 529 function, name, value = self._get_fields(line, 3) 530 init_item(self.function_locals, function, dict) 531 init_item(self.importer.function_locals, function, dict) 532 if name != "{}": 533 self.importer.function_locals[function][name] = \ 534 self.function_locals[function][name] = decode_reference(value) 535 line = f.readline().rstrip() 536 537 def _get_attribute_usage(self, f): 538 f.readline() # "attribute usage:" 539 line = f.readline().rstrip() 540 while line: 541 unit, value = self._get_fields(line) 542 init_item(self.attr_usage, unit, dict) 543 self.usage_from_cache(value, self.attr_usage[unit]) 544 line = f.readline().rstrip() 545 546 def _get_attr_accesses(self, f): 547 f.readline() # "attribute accesses:" 548 line = f.readline().rstrip() 549 while line: 550 name, value = self._get_fields(line) 551 self.attr_accesses[name] = set(value.split(", ")) 552 line = f.readline().rstrip() 553 554 def _get_const_accesses(self, f): 555 f.readline() # "constant accesses:" 556 line = f.readline().rstrip() 557 while line: 558 name, original_name, attrnames, objpath, ref, remaining = self._get_fields(line, 6) 559 if attrnames == "{}": attrnames = None 560 init_item(self.const_accesses, name, dict) 561 self.const_accesses[name][(original_name, attrnames)] = (objpath, decode_reference(ref), remaining != "{}" and remaining or "") 562 line = f.readline().rstrip() 563 564 def _get_attr_accessors(self, f): 565 f.readline() # "attribute access usage:" 566 line = f.readline().rstrip() 567 while line: 568 objpath, name, attrname, value = self._get_fields(line, 4) 569 if attrname == "{}": attrname = None 570 access = name, attrname 571 init_item(self.attr_accessors, objpath, dict) 572 init_item(self.attr_accessors[objpath], access, list) 573 positions = map(int, value.split(", ")) 574 self.attr_accessors[objpath][access].append(positions) 575 line = f.readline().rstrip() 576 577 def _get_attr_access_modifiers(self, f): 578 f.readline() # "attribute access modifiers:" 579 line = f.readline().rstrip() 580 while line: 581 objpath, name, attrnames, value = self._get_fields(line, 4) 582 if name == "{}": name = None 583 if attrnames == "{}": attrnames = None 584 access = name, attrnames 585 init_item(self.attr_access_modifiers, objpath, dict) 586 init_item(self.attr_access_modifiers[objpath], access, list) 587 modifiers = decode_modifiers(value) 588 self.attr_access_modifiers[objpath][access] = modifiers 589 line = f.readline().rstrip() 590 591 def _get_constant_literals(self, f): 592 f.readline() # "constant literals:" 593 line = f.readline().rstrip() 594 last_path = None 595 n = None 596 while line: 597 path, value_type, encoding, value = self._get_fields(line, 4) 598 if path != last_path: 599 n = 0 600 last_path = path 601 else: 602 n += 1 603 init_item(self.constants, path, dict) 604 value = eval(value) 605 encoding = encoding != "{}" and encoding or None 606 self.constants[path][(value, value_type, encoding)] = n 607 line = f.readline().rstrip() 608 609 def _get_constant_values(self, f): 610 f.readline() # "constant values:" 611 line = f.readline().rstrip() 612 while line: 613 name, value_type, encoding, value = self._get_fields(line, 4) 614 value = eval(value) 615 encoding = encoding != "{}" and encoding or None 616 self.constant_values[name] = value, value_type, encoding 617 line = f.readline().rstrip() 618 619 def _get_exception_namespaces(self, f): 620 f.readline() # "exception namespaces:" 621 value = f.readline().rstrip() 622 self.exception_namespaces = value and set(value.split(", ")) or set() 623 f.readline() 624 625 # Generic parsing methods. 626 627 def from_lines(self, f, d): 628 629 "Read lines from 'f', populating 'd'." 630 631 f.readline() # section heading 632 line = f.readline().rstrip() 633 while line: 634 name, value = self._get_fields(line) 635 d[name] = set(value and value.split(", ") or []) 636 line = f.readline().rstrip() 637 638 def usage_from_cache(self, value, mapping): 639 640 """ 641 Interpret the given 'value' containing name and usage information, 642 storing the information in the given 'mapping'. 643 """ 644 645 local, usage = self._get_fields(value) 646 init_item(mapping, local, list) 647 self._usage_from_cache(mapping[local], usage) 648 649 def _usage_from_cache(self, d, usage): 650 651 # Interpret descriptions of each version of the name. 652 653 all_usages = set() 654 for attrnames in usage.split("; "): 655 if attrnames == "{}": 656 all_attrnames = () 657 else: 658 all_attrnames = decode_usage(attrnames) 659 all_usages.add(all_attrnames) 660 661 d.append(all_usages) 662 663 def _get_fields(self, s, n=2): 664 result = s.split(" ", n-1) 665 if len(result) == n: 666 return result 667 else: 668 return tuple(result) + tuple([""] * (n - len(result))) 669 670 class CacheWritingModule: 671 672 """ 673 A mix-in providing cache-writing support, to be combined with BasicModule. 674 """ 675 676 def to_cache(self, filename): 677 678 """ 679 Write a cached representation of the inspected module to the file having 680 the given 'filename'. 681 """ 682 683 f = open(filename, "w") 684 try: 685 print >>f, self.filename 686 687 print >>f 688 print >>f, "imports:" 689 required = list(self.required) 690 required.sort() 691 print >>f, required and ", ".join(required) or "{}" 692 imports = list(self.imports) 693 imports.sort() 694 print >>f, imports and ", ".join(imports) or "{}" 695 696 print >>f 697 print >>f, "deferred:" 698 deferred = map(str, set(self.deferred)) 699 deferred.sort() 700 print >>f, ", ".join(deferred) 701 702 print >>f 703 print >>f, "special:" 704 names = self.special.keys() 705 names.sort() 706 for name in names: 707 ref, paths = self.special[name] 708 print >>f, name, ref, ", ".join(paths) 709 710 print >>f 711 print >>f, "members:" 712 objects = self.objects.keys() 713 objects.sort() 714 for name in objects: 715 print >>f, name, self.objects[name] 716 717 print >>f 718 print >>f, "class relationships:" 719 classes = self.classes.keys() 720 classes.sort() 721 for class_ in classes: 722 bases = self.classes[class_] 723 if bases: 724 print >>f, class_, ", ".join(map(str, bases)) 725 else: 726 print >>f, class_ 727 728 self.to_lines(f, "instance attributes:", self.instance_attrs) 729 730 print >>f 731 print >>f, "instance attribute constants:" 732 classes = self.instance_attr_constants.items() 733 classes.sort() 734 for name, attrs in classes: 735 attrs = attrs.items() 736 attrs.sort() 737 for attrname, ref in attrs: 738 print >>f, name, attrname, ref 739 740 self.to_lines(f, "names used:", self.names_used) 741 742 print >>f 743 print >>f, "name references:" 744 refs = self.name_references.items() 745 refs.sort() 746 for name, ref in refs: 747 print >>f, name, ref 748 749 print >>f 750 print >>f, "initialised names:" 751 assignments = self.initialised_names.items() 752 assignments.sort() 753 for (path, name), refs in assignments: 754 versions = refs.items() 755 versions.sort() 756 for version, ref in versions: 757 print >>f, path, name, version, ref 758 759 print >>f 760 print >>f, "aliased names:" 761 assignments = self.aliased_names.items() 762 assignments.sort() 763 for (path, name), aliases in assignments: 764 versions = aliases.items() 765 versions.sort() 766 for version, alias in versions: 767 original_path, original_name, attrnames, number = alias 768 print >>f, path, name, version, original_path, original_name, attrnames or "{}", number is None and "{}" or number 769 770 print >>f 771 print >>f, "function parameters:" 772 functions = self.function_parameters.keys() 773 functions.sort() 774 for function in functions: 775 parameters = self.function_parameters[function] 776 if parameters: 777 print >>f, function, ", ".join(parameters) 778 else: 779 print >>f, function, "{}" 780 781 print >>f 782 print >>f, "function default parameters:" 783 functions = self.function_defaults.keys() 784 functions.sort() 785 for function in functions: 786 parameters = self.function_defaults[function] 787 if parameters: 788 print >>f, function, ", ".join([("%s=%s" % (name, default)) for (name, default) in parameters]) 789 else: 790 print >>f, function, "{}" 791 792 print >>f 793 print >>f, "function locals:" 794 functions = self.function_locals.keys() 795 functions.sort() 796 for function in functions: 797 names = self.function_locals[function].items() 798 if names: 799 names.sort() 800 for name, value in names: 801 print >>f, function, name, value 802 else: 803 print >>f, function, "{}" 804 805 self.to_lines(f, "scope globals:", self.scope_globals) 806 807 print >>f 808 print >>f, "attribute usage:" 809 units = self.attr_usage.keys() 810 units.sort() 811 for unit in units: 812 d = self.attr_usage[unit] 813 self.usage_to_cache(d, f, unit) 814 815 print >>f 816 print >>f, "attribute accesses:" 817 paths = self.attr_accesses.keys() 818 paths.sort() 819 for path in paths: 820 accesses = list(self.attr_accesses[path]) 821 accesses.sort() 822 print >>f, path, ", ".join(accesses) 823 824 print >>f 825 print >>f, "constant accesses:" 826 paths = self.const_accesses.keys() 827 paths.sort() 828 for path in paths: 829 accesses = self.const_accesses[path].items() 830 accesses.sort() 831 for (original_name, attrnames), (objpath, ref, remaining_attrnames) in accesses: 832 print >>f, path, original_name, attrnames, objpath, ref, remaining_attrnames or "{}" 833 834 print >>f 835 print >>f, "attribute access usage:" 836 paths = self.attr_accessors.keys() 837 paths.sort() 838 for path in paths: 839 all_accesses = self.attr_accessors[path].items() 840 all_accesses.sort() 841 for (name, attrname), accesses in all_accesses: 842 for positions in accesses: 843 positions = map(str, positions) 844 print >>f, path, name, attrname or "{}", ", ".join(positions) 845 846 print >>f 847 print >>f, "attribute access modifiers:" 848 paths = self.attr_access_modifiers.keys() 849 paths.sort() 850 for path in paths: 851 all_accesses = self.attr_access_modifiers[path].items() 852 all_accesses.sort() 853 for (name, attrnames), modifiers in all_accesses: 854 print >>f, path, name or "{}", attrnames or "{}", encode_modifiers(modifiers) 855 856 print >>f 857 print >>f, "constant literals:" 858 paths = self.constants.keys() 859 paths.sort() 860 for path in paths: 861 constants = [] 862 for (value, value_type, encoding), n in self.constants[path].items(): 863 constants.append((n, value_type, encoding, value)) 864 constants.sort() 865 for n, value_type, encoding, value in constants: 866 print >>f, path, value_type, encoding or "{}", repr(value) 867 868 print >>f 869 print >>f, "constant values:" 870 names = self.constant_values.keys() 871 names.sort() 872 for name in names: 873 value, value_type, encoding = self.constant_values[name] 874 print >>f, name, value_type, encoding or "{}", repr(value) 875 876 print >>f 877 print >>f, "exception namespaces:" 878 paths = list(self.exception_namespaces) 879 paths.sort() 880 print >>f, ", ".join(paths) 881 882 finally: 883 f.close() 884 885 def to_lines(self, f, heading, d): 886 887 "Write lines to 'f' with the given 'heading', using 'd'." 888 889 print >>f 890 print >>f, heading 891 keys = d.keys() 892 keys.sort() 893 for key in keys: 894 attrs = list(d[key]) 895 if attrs: 896 attrs.sort() 897 print >>f, key, ", ".join(attrs) 898 899 def usage_to_cache(self, details, f, prefix): 900 901 "Write the given namespace usage details to the cache." 902 903 names = list(details.keys()) 904 if names: 905 names.sort() 906 for name in names: 907 if details[name]: 908 909 # Produce descriptions for each version of the name. 910 911 for version in details[name]: 912 all_usages = [] 913 for usage in version: 914 all_usages.append(encode_usage(usage)) 915 916 print >>f, "%s %s %s" % (prefix, name, "; ".join(all_usages)) 917 918 # vim: tabstop=4 expandtab shiftwidth=4