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 self._get_return_values(f) 406 407 finally: 408 f.close() 409 410 def complete(self): 411 self.propagate() 412 413 def _get_imports(self, f): 414 f.readline() # "imports:" 415 line = f.readline().strip() 416 self.required = line != "{}" and set(line.split(", ")) or set() 417 line = f.readline().strip() 418 self.imports = line != "{}" and set(line.split(", ")) or set() 419 f.readline() 420 421 for name in self.required: 422 self.queue_module(name, True) 423 for name in self.imports: 424 self.queue_module(name) 425 426 def _get_deferred(self, f): 427 f.readline() # "deferred:" 428 line = f.readline().rstrip() 429 self.deferred = map(decode_reference, line.split(", ")) 430 f.readline() 431 432 def _get_special(self, f): 433 f.readline() # "special:" 434 line = f.readline().rstrip() 435 while line: 436 name, ref, paths = self._get_fields(line, 3) 437 self.special[name] = decode_reference(ref), paths.split(", ") 438 line = f.readline().rstrip() 439 440 def _get_members(self, f): 441 f.readline() # "members:" 442 line = f.readline().rstrip() 443 while line: 444 name, ref = line.split(" ", 1) 445 self.set_object(name, ref) 446 line = f.readline().rstrip() 447 448 def _get_class_relationships(self, f): 449 f.readline() # "class relationships:" 450 line = f.readline().rstrip() 451 while line: 452 name, value = self._get_fields(line) 453 values = value and value.split(", ") or [] 454 self.importer.classes[name] = self.classes[name] = map(decode_reference, values) 455 self.importer.subclasses[name] = set() 456 line = f.readline().rstrip() 457 458 def _get_instance_attrs(self, f): 459 f.readline() # "instance attributes:" 460 line = f.readline().rstrip() 461 while line: 462 name, value = self._get_fields(line) 463 self.importer.all_instance_attrs[name] = self.instance_attrs[name] = set(value and value.split(", ") or []) 464 line = f.readline().rstrip() 465 466 def _get_instance_attr_constants(self, f): 467 f.readline() # "instance attribute constants:" 468 line = f.readline().rstrip() 469 while line: 470 name, attrname, ref = self._get_fields(line, 3) 471 init_item(self.instance_attr_constants, name, dict) 472 self.instance_attr_constants[name][attrname] = decode_reference(ref) 473 line = f.readline().rstrip() 474 475 def _get_name_references(self, f): 476 f.readline() # "name references:" 477 line = f.readline().rstrip() 478 while line: 479 name, ref = self._get_fields(line) 480 self.importer.all_name_references[name] = self.name_references[name] = decode_reference(ref) 481 line = f.readline().rstrip() 482 483 def _get_initialised_names(self, f): 484 f.readline() # "initialised names:" 485 line = f.readline().rstrip() 486 while line: 487 path, name, version, value = self._get_fields(line, 4) 488 init_item(self.initialised_names, (path, name), dict) 489 self.initialised_names[(path, name)][int(version)] = decode_reference(value) 490 line = f.readline().rstrip() 491 492 def _get_aliased_names(self, f): 493 f.readline() # "aliased names:" 494 line = f.readline().rstrip() 495 while line: 496 path, name, version, original_path, original_name, attrnames, number = self._get_fields(line, 7) 497 init_item(self.aliased_names, (path, name), dict) 498 if number == "{}": number = None 499 else: number = int(number) 500 self.aliased_names[(path, name)][int(version)] = (original_path, original_name, attrnames != "{}" and attrnames or None, number) 501 line = f.readline().rstrip() 502 503 def _get_function_parameters(self, f): 504 f.readline() # "function parameters:" 505 line = f.readline().rstrip() 506 while line: 507 function, names = self._get_fields(line) 508 self.importer.function_parameters[function] = \ 509 self.function_parameters[function] = names != "{}" and names.split(", ") or [] 510 line = f.readline().rstrip() 511 512 def _get_function_defaults(self, f): 513 f.readline() # "function default parameters:" 514 line = f.readline().rstrip() 515 while line: 516 function, defaults = self._get_fields(line) 517 self.importer.function_defaults[function] = \ 518 self.function_defaults[function] = l = [] 519 if defaults != "{}": 520 for value in defaults.split(", "): 521 name, default = value.split("=") 522 default = decode_reference(default) 523 l.append((name, default)) 524 line = f.readline().rstrip() 525 526 def _get_function_locals(self, f): 527 f.readline() # "function locals:" 528 line = f.readline().rstrip() 529 while line: 530 function, name, value = self._get_fields(line, 3) 531 init_item(self.function_locals, function, dict) 532 init_item(self.importer.function_locals, function, dict) 533 if name != "{}": 534 self.importer.function_locals[function][name] = \ 535 self.function_locals[function][name] = decode_reference(value) 536 line = f.readline().rstrip() 537 538 def _get_attribute_usage(self, f): 539 f.readline() # "attribute usage:" 540 line = f.readline().rstrip() 541 while line: 542 unit, value = self._get_fields(line) 543 init_item(self.attr_usage, unit, dict) 544 self.usage_from_cache(value, self.attr_usage[unit]) 545 line = f.readline().rstrip() 546 547 def _get_attr_accesses(self, f): 548 f.readline() # "attribute accesses:" 549 line = f.readline().rstrip() 550 while line: 551 name, value = self._get_fields(line) 552 self.attr_accesses[name] = set(value.split(", ")) 553 line = f.readline().rstrip() 554 555 def _get_const_accesses(self, f): 556 f.readline() # "constant accesses:" 557 line = f.readline().rstrip() 558 while line: 559 name, original_name, attrnames, objpath, ref, remaining = self._get_fields(line, 6) 560 if attrnames == "{}": attrnames = None 561 init_item(self.const_accesses, name, dict) 562 self.const_accesses[name][(original_name, attrnames)] = (objpath, decode_reference(ref), remaining != "{}" and remaining or "") 563 line = f.readline().rstrip() 564 565 def _get_attr_accessors(self, f): 566 f.readline() # "attribute access usage:" 567 line = f.readline().rstrip() 568 while line: 569 objpath, name, attrname, value = self._get_fields(line, 4) 570 if attrname == "{}": attrname = None 571 access = name, attrname 572 init_item(self.attr_accessors, objpath, dict) 573 init_item(self.attr_accessors[objpath], access, list) 574 positions = map(int, value.split(", ")) 575 self.attr_accessors[objpath][access].append(positions) 576 line = f.readline().rstrip() 577 578 def _get_attr_access_modifiers(self, f): 579 f.readline() # "attribute access modifiers:" 580 line = f.readline().rstrip() 581 while line: 582 objpath, name, attrnames, value = self._get_fields(line, 4) 583 if name == "{}": name = None 584 if attrnames == "{}": attrnames = None 585 access = name, attrnames 586 init_item(self.attr_access_modifiers, objpath, dict) 587 init_item(self.attr_access_modifiers[objpath], access, list) 588 modifiers = decode_modifiers(value) 589 self.attr_access_modifiers[objpath][access] = modifiers 590 line = f.readline().rstrip() 591 592 def _get_constant_literals(self, f): 593 f.readline() # "constant literals:" 594 line = f.readline().rstrip() 595 last_path = None 596 n = None 597 while line: 598 path, value_type, encoding, value = self._get_fields(line, 4) 599 if path != last_path: 600 n = 0 601 last_path = path 602 else: 603 n += 1 604 init_item(self.constants, path, dict) 605 value = eval(value) 606 encoding = encoding != "{}" and encoding or None 607 self.constants[path][(value, value_type, encoding)] = n 608 line = f.readline().rstrip() 609 610 def _get_constant_values(self, f): 611 f.readline() # "constant values:" 612 line = f.readline().rstrip() 613 while line: 614 name, value_type, encoding, value = self._get_fields(line, 4) 615 value = eval(value) 616 encoding = encoding != "{}" and encoding or None 617 self.constant_values[name] = value, value_type, encoding 618 line = f.readline().rstrip() 619 620 def _get_exception_namespaces(self, f): 621 f.readline() # "exception namespaces:" 622 value = f.readline().rstrip() 623 self.exception_namespaces = value and set(value.split(", ")) or set() 624 f.readline() 625 626 def _get_return_values(self, f): 627 f.readline() # "return values:" 628 line = f.readline().rstrip() 629 while line: 630 path, values = self._get_fields(line) 631 values = values.split(", ") 632 self.return_values[path] = map(decode_reference, values) 633 line = f.readline().rstrip() 634 635 # Generic parsing methods. 636 637 def from_lines(self, f, d): 638 639 "Read lines from 'f', populating 'd'." 640 641 f.readline() # section heading 642 line = f.readline().rstrip() 643 while line: 644 name, value = self._get_fields(line) 645 d[name] = set(value and value.split(", ") or []) 646 line = f.readline().rstrip() 647 648 def usage_from_cache(self, value, mapping): 649 650 """ 651 Interpret the given 'value' containing name and usage information, 652 storing the information in the given 'mapping'. 653 """ 654 655 local, usage = self._get_fields(value) 656 init_item(mapping, local, list) 657 self._usage_from_cache(mapping[local], usage) 658 659 def _usage_from_cache(self, d, usage): 660 661 # Interpret descriptions of each version of the name. 662 663 all_usages = set() 664 for attrnames in usage.split("; "): 665 if attrnames == "{}": 666 all_attrnames = () 667 else: 668 all_attrnames = decode_usage(attrnames) 669 all_usages.add(all_attrnames) 670 671 d.append(all_usages) 672 673 def _get_fields(self, s, n=2): 674 result = s.split(" ", n-1) 675 if len(result) == n: 676 return result 677 else: 678 return tuple(result) + tuple([""] * (n - len(result))) 679 680 class CacheWritingModule: 681 682 """ 683 A mix-in providing cache-writing support, to be combined with BasicModule. 684 """ 685 686 def to_cache(self, filename): 687 688 """ 689 Write a cached representation of the inspected module to the file having 690 the given 'filename'. 691 """ 692 693 f = open(filename, "w") 694 try: 695 print >>f, self.filename 696 697 print >>f 698 print >>f, "imports:" 699 required = list(self.required) 700 required.sort() 701 print >>f, required and ", ".join(required) or "{}" 702 imports = list(self.imports) 703 imports.sort() 704 print >>f, imports and ", ".join(imports) or "{}" 705 706 print >>f 707 print >>f, "deferred:" 708 deferred = map(str, set(self.deferred)) 709 deferred.sort() 710 print >>f, ", ".join(deferred) 711 712 print >>f 713 print >>f, "special:" 714 names = self.special.keys() 715 names.sort() 716 for name in names: 717 ref, paths = self.special[name] 718 print >>f, name, ref, ", ".join(paths) 719 720 print >>f 721 print >>f, "members:" 722 objects = self.objects.keys() 723 objects.sort() 724 for name in objects: 725 print >>f, name, self.objects[name] 726 727 print >>f 728 print >>f, "class relationships:" 729 classes = self.classes.keys() 730 classes.sort() 731 for class_ in classes: 732 bases = self.classes[class_] 733 if bases: 734 print >>f, class_, ", ".join(map(str, bases)) 735 else: 736 print >>f, class_ 737 738 self.to_lines(f, "instance attributes:", self.instance_attrs) 739 740 print >>f 741 print >>f, "instance attribute constants:" 742 classes = self.instance_attr_constants.items() 743 classes.sort() 744 for name, attrs in classes: 745 attrs = attrs.items() 746 attrs.sort() 747 for attrname, ref in attrs: 748 print >>f, name, attrname, ref 749 750 self.to_lines(f, "names used:", self.names_used) 751 752 print >>f 753 print >>f, "name references:" 754 refs = self.name_references.items() 755 refs.sort() 756 for name, ref in refs: 757 print >>f, name, ref 758 759 print >>f 760 print >>f, "initialised names:" 761 assignments = self.initialised_names.items() 762 assignments.sort() 763 for (path, name), refs in assignments: 764 versions = refs.items() 765 versions.sort() 766 for version, ref in versions: 767 print >>f, path, name, version, ref 768 769 print >>f 770 print >>f, "aliased names:" 771 assignments = self.aliased_names.items() 772 assignments.sort() 773 for (path, name), aliases in assignments: 774 versions = aliases.items() 775 versions.sort() 776 for version, alias in versions: 777 original_path, original_name, attrnames, number = alias 778 print >>f, path, name, version, original_path, original_name, attrnames or "{}", number is None and "{}" or number 779 780 print >>f 781 print >>f, "function parameters:" 782 functions = self.function_parameters.keys() 783 functions.sort() 784 for function in functions: 785 parameters = self.function_parameters[function] 786 if parameters: 787 print >>f, function, ", ".join(parameters) 788 else: 789 print >>f, function, "{}" 790 791 print >>f 792 print >>f, "function default parameters:" 793 functions = self.function_defaults.keys() 794 functions.sort() 795 for function in functions: 796 parameters = self.function_defaults[function] 797 if parameters: 798 print >>f, function, ", ".join([("%s=%s" % (name, default)) for (name, default) in parameters]) 799 else: 800 print >>f, function, "{}" 801 802 print >>f 803 print >>f, "function locals:" 804 functions = self.function_locals.keys() 805 functions.sort() 806 for function in functions: 807 names = self.function_locals[function].items() 808 if names: 809 names.sort() 810 for name, value in names: 811 print >>f, function, name, value 812 else: 813 print >>f, function, "{}" 814 815 self.to_lines(f, "scope globals:", self.scope_globals) 816 817 print >>f 818 print >>f, "attribute usage:" 819 units = self.attr_usage.keys() 820 units.sort() 821 for unit in units: 822 d = self.attr_usage[unit] 823 self.usage_to_cache(d, f, unit) 824 825 print >>f 826 print >>f, "attribute accesses:" 827 paths = self.attr_accesses.keys() 828 paths.sort() 829 for path in paths: 830 accesses = list(self.attr_accesses[path]) 831 accesses.sort() 832 print >>f, path, ", ".join(accesses) 833 834 print >>f 835 print >>f, "constant accesses:" 836 paths = self.const_accesses.keys() 837 paths.sort() 838 for path in paths: 839 accesses = self.const_accesses[path].items() 840 accesses.sort() 841 for (original_name, attrnames), (objpath, ref, remaining_attrnames) in accesses: 842 print >>f, path, original_name, attrnames, objpath, ref, remaining_attrnames or "{}" 843 844 print >>f 845 print >>f, "attribute access usage:" 846 paths = self.attr_accessors.keys() 847 paths.sort() 848 for path in paths: 849 all_accesses = self.attr_accessors[path].items() 850 all_accesses.sort() 851 for (name, attrname), accesses in all_accesses: 852 for positions in accesses: 853 positions = map(str, positions) 854 print >>f, path, name, attrname or "{}", ", ".join(positions) 855 856 print >>f 857 print >>f, "attribute access modifiers:" 858 paths = self.attr_access_modifiers.keys() 859 paths.sort() 860 for path in paths: 861 all_accesses = self.attr_access_modifiers[path].items() 862 all_accesses.sort() 863 for (name, attrnames), modifiers in all_accesses: 864 print >>f, path, name or "{}", attrnames or "{}", encode_modifiers(modifiers) 865 866 print >>f 867 print >>f, "constant literals:" 868 paths = self.constants.keys() 869 paths.sort() 870 for path in paths: 871 constants = [] 872 for (value, value_type, encoding), n in self.constants[path].items(): 873 constants.append((n, value_type, encoding, value)) 874 constants.sort() 875 for n, value_type, encoding, value in constants: 876 print >>f, path, value_type, encoding or "{}", repr(value) 877 878 print >>f 879 print >>f, "constant values:" 880 names = self.constant_values.keys() 881 names.sort() 882 for name in names: 883 value, value_type, encoding = self.constant_values[name] 884 print >>f, name, value_type, encoding or "{}", repr(value) 885 886 print >>f 887 print >>f, "exception namespaces:" 888 paths = list(self.exception_namespaces) 889 paths.sort() 890 print >>f, ", ".join(paths) 891 892 print >>f 893 print >>f, "return values:" 894 paths = self.return_values.keys() 895 paths.sort() 896 for path in paths: 897 print >>f, path, ", ".join(map(str, self.return_values[path])) 898 899 finally: 900 f.close() 901 902 def to_lines(self, f, heading, d): 903 904 "Write lines to 'f' with the given 'heading', using 'd'." 905 906 print >>f 907 print >>f, heading 908 keys = d.keys() 909 keys.sort() 910 for key in keys: 911 attrs = list(d[key]) 912 if attrs: 913 attrs.sort() 914 print >>f, key, ", ".join(attrs) 915 916 def usage_to_cache(self, details, f, prefix): 917 918 "Write the given namespace usage details to the cache." 919 920 names = list(details.keys()) 921 if names: 922 names.sort() 923 for name in names: 924 if details[name]: 925 926 # Produce descriptions for each version of the name. 927 928 for version in details[name]: 929 all_usages = [] 930 for usage in version: 931 all_usages.append(encode_usage(usage)) 932 933 print >>f, "%s %s %s" % (prefix, name, "; ".join(all_usages)) 934 935 # vim: tabstop=4 expandtab shiftwidth=4