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