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