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