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