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 # Attribute usage at module and function levels. 75 76 self.attr_usage = {} 77 self.name_initialisers = {} 78 79 # General attribute access expressions. 80 81 self.attr_accesses = {} 82 self.const_accesses = {} 83 84 # Attribute accessor definition details. 85 86 self.attr_accessors = {} 87 88 # Assignment details for accesses. 89 90 self.attr_access_modifiers = {} 91 92 # Name resolution details. 93 94 self.name_references = {} # references to globals 95 96 # Initialisation-related details. 97 98 self.initialised_names = {} 99 self.aliased_names = {} 100 101 # Return values for functions in this module. 102 103 self.return_values = {} 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 d = self.aliased_names[(path, name)] 500 init_item(d, int(version), list) 501 d[int(version)].append((original_path, original_name, attrnames != "{}" and attrnames or None, number)) 502 line = f.readline().rstrip() 503 504 def _get_function_parameters(self, f): 505 f.readline() # "function parameters:" 506 line = f.readline().rstrip() 507 while line: 508 function, names = self._get_fields(line) 509 self.importer.function_parameters[function] = \ 510 self.function_parameters[function] = names != "{}" and names.split(", ") or [] 511 line = f.readline().rstrip() 512 513 def _get_function_defaults(self, f): 514 f.readline() # "function default parameters:" 515 line = f.readline().rstrip() 516 while line: 517 function, defaults = self._get_fields(line) 518 self.importer.function_defaults[function] = \ 519 self.function_defaults[function] = l = [] 520 if defaults != "{}": 521 for value in defaults.split(", "): 522 name, default = value.split("=") 523 default = decode_reference(default) 524 l.append((name, default)) 525 line = f.readline().rstrip() 526 527 def _get_function_locals(self, f): 528 f.readline() # "function locals:" 529 line = f.readline().rstrip() 530 while line: 531 function, name, value = self._get_fields(line, 3) 532 init_item(self.function_locals, function, dict) 533 init_item(self.importer.function_locals, function, dict) 534 if name != "{}": 535 self.importer.function_locals[function][name] = \ 536 self.function_locals[function][name] = decode_reference(value) 537 line = f.readline().rstrip() 538 539 def _get_attribute_usage(self, f): 540 f.readline() # "attribute usage:" 541 line = f.readline().rstrip() 542 while line: 543 unit, value = self._get_fields(line) 544 init_item(self.attr_usage, unit, dict) 545 self.usage_from_cache(value, self.attr_usage[unit]) 546 line = f.readline().rstrip() 547 548 def _get_attr_accesses(self, f): 549 f.readline() # "attribute accesses:" 550 line = f.readline().rstrip() 551 while line: 552 name, value = self._get_fields(line) 553 self.attr_accesses[name] = set(value.split(", ")) 554 line = f.readline().rstrip() 555 556 def _get_const_accesses(self, f): 557 f.readline() # "constant accesses:" 558 line = f.readline().rstrip() 559 while line: 560 name, original_name, attrnames, objpath, ref, remaining = self._get_fields(line, 6) 561 if attrnames == "{}": attrnames = None 562 init_item(self.const_accesses, name, dict) 563 self.const_accesses[name][(original_name, attrnames)] = (objpath, decode_reference(ref), remaining != "{}" and remaining or "") 564 line = f.readline().rstrip() 565 566 def _get_attr_accessors(self, f): 567 f.readline() # "attribute access usage:" 568 line = f.readline().rstrip() 569 while line: 570 objpath, name, attrname, value = self._get_fields(line, 4) 571 if attrname == "{}": attrname = None 572 access = name, attrname 573 init_item(self.attr_accessors, objpath, dict) 574 init_item(self.attr_accessors[objpath], access, list) 575 positions = map(int, value.split(", ")) 576 self.attr_accessors[objpath][access].append(positions) 577 line = f.readline().rstrip() 578 579 def _get_attr_access_modifiers(self, f): 580 f.readline() # "attribute access modifiers:" 581 line = f.readline().rstrip() 582 while line: 583 objpath, name, attrnames, value = self._get_fields(line, 4) 584 if name == "{}": name = None 585 if attrnames == "{}": attrnames = None 586 access = name, attrnames 587 init_item(self.attr_access_modifiers, objpath, dict) 588 init_item(self.attr_access_modifiers[objpath], access, list) 589 modifiers = decode_modifiers(value) 590 self.attr_access_modifiers[objpath][access] = modifiers 591 line = f.readline().rstrip() 592 593 def _get_constant_literals(self, f): 594 f.readline() # "constant literals:" 595 line = f.readline().rstrip() 596 last_path = None 597 n = None 598 while line: 599 path, value_type, encoding, value = self._get_fields(line, 4) 600 if path != last_path: 601 n = 0 602 last_path = path 603 else: 604 n += 1 605 init_item(self.constants, path, dict) 606 value = eval(value) 607 encoding = encoding != "{}" and encoding or None 608 self.constants[path][(value, value_type, encoding)] = n 609 line = f.readline().rstrip() 610 611 def _get_constant_values(self, f): 612 f.readline() # "constant values:" 613 line = f.readline().rstrip() 614 while line: 615 name, value_type, encoding, value = self._get_fields(line, 4) 616 value = eval(value) 617 encoding = encoding != "{}" and encoding or None 618 self.constant_values[name] = value, value_type, encoding 619 line = f.readline().rstrip() 620 621 def _get_exception_namespaces(self, f): 622 f.readline() # "exception namespaces:" 623 value = f.readline().rstrip() 624 self.exception_namespaces = value and set(value.split(", ")) or set() 625 f.readline() 626 627 # Generic parsing methods. 628 629 def from_lines(self, f, d): 630 631 "Read lines from 'f', populating 'd'." 632 633 f.readline() # section heading 634 line = f.readline().rstrip() 635 while line: 636 name, value = self._get_fields(line) 637 d[name] = set(value and value.split(", ") or []) 638 line = f.readline().rstrip() 639 640 def usage_from_cache(self, value, mapping): 641 642 """ 643 Interpret the given 'value' containing name and usage information, 644 storing the information in the given 'mapping'. 645 """ 646 647 local, usage = self._get_fields(value) 648 init_item(mapping, local, list) 649 self._usage_from_cache(mapping[local], usage) 650 651 def _usage_from_cache(self, d, usage): 652 653 # Interpret descriptions of each version of the name. 654 655 all_usages = set() 656 for attrnames in usage.split("; "): 657 if attrnames == "{}": 658 all_attrnames = () 659 else: 660 all_attrnames = decode_usage(attrnames) 661 all_usages.add(all_attrnames) 662 663 d.append(all_usages) 664 665 def _get_fields(self, s, n=2): 666 result = s.split(" ", n-1) 667 if len(result) == n: 668 return result 669 else: 670 return tuple(result) + tuple([""] * (n - len(result))) 671 672 class CacheWritingModule: 673 674 """ 675 A mix-in providing cache-writing support, to be combined with BasicModule. 676 """ 677 678 def to_cache(self, filename): 679 680 """ 681 Write a cached representation of the inspected module to the file having 682 the given 'filename'. 683 """ 684 685 f = open(filename, "w") 686 try: 687 print >>f, self.filename 688 689 print >>f 690 print >>f, "imports:" 691 required = list(self.required) 692 required.sort() 693 print >>f, required and ", ".join(required) or "{}" 694 imports = list(self.imports) 695 imports.sort() 696 print >>f, imports and ", ".join(imports) or "{}" 697 698 print >>f 699 print >>f, "deferred:" 700 deferred = map(str, set(self.deferred)) 701 deferred.sort() 702 print >>f, ", ".join(deferred) 703 704 print >>f 705 print >>f, "special:" 706 names = self.special.keys() 707 names.sort() 708 for name in names: 709 ref, paths = self.special[name] 710 print >>f, name, ref, ", ".join(paths) 711 712 print >>f 713 print >>f, "members:" 714 objects = self.objects.keys() 715 objects.sort() 716 for name in objects: 717 print >>f, name, self.objects[name] 718 719 print >>f 720 print >>f, "class relationships:" 721 classes = self.classes.keys() 722 classes.sort() 723 for class_ in classes: 724 bases = self.classes[class_] 725 if bases: 726 print >>f, class_, ", ".join(map(str, bases)) 727 else: 728 print >>f, class_ 729 730 self.to_lines(f, "instance attributes:", self.instance_attrs) 731 732 print >>f 733 print >>f, "instance attribute constants:" 734 classes = self.instance_attr_constants.items() 735 classes.sort() 736 for name, attrs in classes: 737 attrs = attrs.items() 738 attrs.sort() 739 for attrname, ref in attrs: 740 print >>f, name, attrname, ref 741 742 self.to_lines(f, "names used:", self.names_used) 743 744 print >>f 745 print >>f, "name references:" 746 refs = self.name_references.items() 747 refs.sort() 748 for name, ref in refs: 749 print >>f, name, ref 750 751 print >>f 752 print >>f, "initialised names:" 753 assignments = self.initialised_names.items() 754 assignments.sort() 755 for (path, name), refs in assignments: 756 versions = refs.items() 757 versions.sort() 758 for version, ref in versions: 759 print >>f, path, name, version, ref 760 761 print >>f 762 print >>f, "aliased names:" 763 assignments = self.aliased_names.items() 764 assignments.sort() 765 for (path, name), aliases in assignments: 766 versions = aliases.items() 767 versions.sort() 768 for version, version_aliases in versions: 769 for alias in version_aliases: 770 original_path, original_name, attrnames, number = alias 771 print >>f, path, name, version, original_path, original_name, attrnames or "{}", number is None and "{}" or number 772 773 print >>f 774 print >>f, "function parameters:" 775 functions = self.function_parameters.keys() 776 functions.sort() 777 for function in functions: 778 parameters = self.function_parameters[function] 779 if parameters: 780 print >>f, function, ", ".join(parameters) 781 else: 782 print >>f, function, "{}" 783 784 print >>f 785 print >>f, "function default parameters:" 786 functions = self.function_defaults.keys() 787 functions.sort() 788 for function in functions: 789 parameters = self.function_defaults[function] 790 if parameters: 791 print >>f, function, ", ".join([("%s=%s" % (name, default)) for (name, default) in parameters]) 792 else: 793 print >>f, function, "{}" 794 795 print >>f 796 print >>f, "function locals:" 797 functions = self.function_locals.keys() 798 functions.sort() 799 for function in functions: 800 names = self.function_locals[function].items() 801 if names: 802 names.sort() 803 for name, value in names: 804 print >>f, function, name, value 805 else: 806 print >>f, function, "{}" 807 808 self.to_lines(f, "scope globals:", self.scope_globals) 809 810 print >>f 811 print >>f, "attribute usage:" 812 units = self.attr_usage.keys() 813 units.sort() 814 for unit in units: 815 d = self.attr_usage[unit] 816 self.usage_to_cache(d, f, unit) 817 818 print >>f 819 print >>f, "attribute accesses:" 820 paths = self.attr_accesses.keys() 821 paths.sort() 822 for path in paths: 823 accesses = list(self.attr_accesses[path]) 824 accesses.sort() 825 print >>f, path, ", ".join(accesses) 826 827 print >>f 828 print >>f, "constant accesses:" 829 paths = self.const_accesses.keys() 830 paths.sort() 831 for path in paths: 832 accesses = self.const_accesses[path].items() 833 accesses.sort() 834 for (original_name, attrnames), (objpath, ref, remaining_attrnames) in accesses: 835 print >>f, path, original_name, attrnames, objpath, ref, remaining_attrnames or "{}" 836 837 print >>f 838 print >>f, "attribute access usage:" 839 paths = self.attr_accessors.keys() 840 paths.sort() 841 for path in paths: 842 all_accesses = self.attr_accessors[path].items() 843 all_accesses.sort() 844 for (name, attrname), accesses in all_accesses: 845 for positions in accesses: 846 positions = map(str, positions) 847 print >>f, path, name, attrname or "{}", ", ".join(positions) 848 849 print >>f 850 print >>f, "attribute access modifiers:" 851 paths = self.attr_access_modifiers.keys() 852 paths.sort() 853 for path in paths: 854 all_accesses = self.attr_access_modifiers[path].items() 855 all_accesses.sort() 856 for (name, attrnames), modifiers in all_accesses: 857 print >>f, path, name or "{}", attrnames or "{}", encode_modifiers(modifiers) 858 859 print >>f 860 print >>f, "constant literals:" 861 paths = self.constants.keys() 862 paths.sort() 863 for path in paths: 864 constants = [] 865 for (value, value_type, encoding), n in self.constants[path].items(): 866 constants.append((n, value_type, encoding, value)) 867 constants.sort() 868 for n, value_type, encoding, value in constants: 869 print >>f, path, value_type, encoding or "{}", repr(value) 870 871 print >>f 872 print >>f, "constant values:" 873 names = self.constant_values.keys() 874 names.sort() 875 for name in names: 876 value, value_type, encoding = self.constant_values[name] 877 print >>f, name, value_type, encoding or "{}", repr(value) 878 879 print >>f 880 print >>f, "exception namespaces:" 881 paths = list(self.exception_namespaces) 882 paths.sort() 883 print >>f, ", ".join(paths) 884 885 finally: 886 f.close() 887 888 def to_lines(self, f, heading, d): 889 890 "Write lines to 'f' with the given 'heading', using 'd'." 891 892 print >>f 893 print >>f, heading 894 keys = d.keys() 895 keys.sort() 896 for key in keys: 897 attrs = list(d[key]) 898 if attrs: 899 attrs.sort() 900 print >>f, key, ", ".join(attrs) 901 902 def usage_to_cache(self, details, f, prefix): 903 904 "Write the given namespace usage details to the cache." 905 906 names = list(details.keys()) 907 if names: 908 names.sort() 909 for name in names: 910 if details[name]: 911 912 # Produce descriptions for each version of the name. 913 914 for version in details[name]: 915 all_usages = [] 916 for usage in version: 917 all_usages.append(encode_usage(usage)) 918 919 print >>f, "%s %s %s" % (prefix, name, "; ".join(all_usages)) 920 921 # vim: tabstop=4 expandtab shiftwidth=4