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 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_module, init_item, remove_items, CommonModule 24 from encoders import decode_modifier_term, decode_usage, encode_modifiers, encode_usage 25 from referencing import decode_reference, Reference 26 from results import ResolvedNameRef 27 import sys 28 29 class BasicModule(CommonModule): 30 31 "The basic module information." 32 33 def __init__(self, name, importer): 34 CommonModule.__init__(self, name, importer) 35 36 # Import details, primarily for cache output. 37 38 self.imports = set() 39 self.required = set() 40 self.deferred = [] 41 42 # Global name information. 43 44 self.objects = {} 45 self.special = {} 46 47 # Class relationships. 48 49 self.classes = {} 50 51 # Attributes. 52 53 self.class_attrs = {} 54 self.instance_attrs = {} 55 self.instance_attr_constants = {} 56 self.module_attrs = set() 57 58 # Names used in each namespace. 59 60 self.names_used = {} 61 62 # Function details. 63 64 self.function_parameters = {} 65 self.function_defaults = {} 66 self.function_locals = {} 67 self.scope_globals = {} 68 69 # Invocation details. 70 71 self.function_targets = {} 72 self.function_arguments = {} 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 # NOTE: This makes assumptions about the __builtins__ structure. 301 302 modname = get_builtin_module(name) 303 module_name = "__builtins__.%s" % modname 304 305 if self.name != module_name: 306 self.queue_module(module_name, True) 307 308 return Reference("<class>", "__builtins__.%s.%s" % (modname, name)) 309 310 def get_object(self, path, defer=True): 311 312 """ 313 Get the details of an object with the given 'path'. Where the object 314 cannot be resolved, an unresolved reference is returned if 'defer' is 315 set to a true value (the default). Otherwise, None is returned. 316 """ 317 318 if self.objects.has_key(path): 319 return self.objects[path] 320 elif defer: 321 ref = Reference("<depends>", path) 322 self.deferred.append(ref) 323 return ref 324 else: 325 return None 326 327 def import_name_from_module(self, name, module_name): 328 329 "Import 'name' from the module having the given 'module_name'." 330 331 if module_name != self.name: 332 self.queue_module(module_name) 333 ref = Reference("<depends>", "%s.%s" % (module_name, name)) 334 self.deferred.append(ref) 335 return ref 336 337 def add_deferred(self, ref): 338 339 "Record 'ref' as a deferred reference." 340 341 if ref.has_kind("<depends>"): 342 self.deferred.append(ref) 343 344 class CachedModule(BasicModule): 345 346 "A cached module." 347 348 def __repr__(self): 349 return "CachedModule(%r, %r)" % (self.name, self.importer) 350 351 def set_object(self, name, value=None): 352 353 "Set an object with the given 'name' and the given 'value'." 354 355 # Decode any string value, with a new reference being returned even 356 # given a provided reference. 357 358 ref = decode_reference(value, name) 359 self._set_object(name, ref) 360 361 def to_cache(self, filename): 362 363 "Not actually writing the module back to 'filename'." 364 365 pass 366 367 def from_cache(self, filename): 368 369 """ 370 Read a module's details from the file with the given 'filename' as 371 described in the to_cache method of InspectedModule. 372 """ 373 374 f = open(filename) 375 try: 376 self.filename = f.readline().rstrip() 377 378 f.readline() # (empty line) 379 380 self._get_imports(f) 381 self._get_deferred(f) 382 self._get_special(f) 383 self._get_members(f) 384 self._get_class_relationships(f) 385 self._get_instance_attrs(f) 386 self._get_instance_attr_constants(f) 387 self.from_lines(f, self.names_used) # "names used:" 388 self._get_name_references(f) 389 self._get_initialised_names(f) 390 self._get_aliased_names(f) 391 self._get_function_parameters(f) 392 self._get_function_defaults(f) 393 self._get_function_locals(f) 394 self.from_lines(f, self.scope_globals) # "scope globals:" 395 self._get_function_targets(f) 396 self._get_function_arguments(f) 397 self._get_attribute_usage(f) 398 self._get_attr_accesses(f) 399 self._get_const_accesses(f) 400 self._get_attr_accessors(f) 401 self._get_attr_access_modifiers(f) 402 self._get_constant_literals(f) 403 self._get_constant_values(f) 404 405 finally: 406 f.close() 407 408 def complete(self): 409 self.propagate() 410 411 def _get_imports(self, f): 412 f.readline() # "imports:" 413 line = f.readline().strip() 414 self.required = line != "{}" and set(line.split(", ")) or set() 415 line = f.readline().strip() 416 self.imports = line != "{}" and set(line.split(", ")) or set() 417 f.readline() 418 419 for name in self.required: 420 self.queue_module(name, True) 421 for name in self.imports: 422 self.queue_module(name) 423 424 def _get_deferred(self, f): 425 f.readline() # "deferred:" 426 line = f.readline().rstrip() 427 self.deferred = map(decode_reference, line.split(" ")) 428 f.readline() 429 430 def _get_special(self, f): 431 f.readline() # "special:" 432 line = f.readline().rstrip() 433 while line: 434 name, ref, paths = self._get_fields(line, 3) 435 self.special[name] = decode_reference(ref), paths.split(", ") 436 line = f.readline().rstrip() 437 438 def _get_members(self, f): 439 f.readline() # "members:" 440 line = f.readline().rstrip() 441 while line: 442 name, ref = line.split(" ", 1) 443 self.set_object(name, ref) 444 line = f.readline().rstrip() 445 446 def _get_class_relationships(self, f): 447 f.readline() # "class relationships:" 448 line = f.readline().rstrip() 449 while line: 450 name, value = self._get_fields(line) 451 values = value and value.split(", ") or [] 452 self.importer.classes[name] = self.classes[name] = map(decode_reference, values) 453 self.importer.subclasses[name] = set() 454 line = f.readline().rstrip() 455 456 def _get_instance_attrs(self, f): 457 f.readline() # "instance attributes:" 458 line = f.readline().rstrip() 459 while line: 460 name, value = self._get_fields(line) 461 self.importer.all_instance_attrs[name] = self.instance_attrs[name] = set(value and value.split(", ") or []) 462 line = f.readline().rstrip() 463 464 def _get_instance_attr_constants(self, f): 465 f.readline() # "instance attribute constants:" 466 line = f.readline().rstrip() 467 while line: 468 name, attrname, ref = self._get_fields(line, 3) 469 init_item(self.instance_attr_constants, name, dict) 470 self.instance_attr_constants[name][attrname] = decode_reference(ref) 471 line = f.readline().rstrip() 472 473 def _get_name_references(self, f): 474 f.readline() # "name references:" 475 line = f.readline().rstrip() 476 while line: 477 name, ref = self._get_fields(line) 478 self.importer.all_name_references[name] = self.name_references[name] = decode_reference(ref) 479 line = f.readline().rstrip() 480 481 def _get_initialised_names(self, f): 482 f.readline() # "initialised names:" 483 line = f.readline().rstrip() 484 while line: 485 name, version, value = self._get_fields(line, 3) 486 init_item(self.initialised_names, name, dict) 487 self.initialised_names[name][int(version)] = decode_reference(value) 488 line = f.readline().rstrip() 489 490 def _get_aliased_names(self, f): 491 f.readline() # "aliased names:" 492 line = f.readline().rstrip() 493 while line: 494 name, version, original_name, attrnames, number = self._get_fields(line, 5) 495 init_item(self.aliased_names, name, dict) 496 if number == "{}": number = None 497 else: number = int(number) 498 self.aliased_names[name][int(version)] = (original_name, attrnames != "{}" and attrnames or None, number) 499 line = f.readline().rstrip() 500 501 def _get_function_parameters(self, f): 502 f.readline() # "function parameters:" 503 line = f.readline().rstrip() 504 while line: 505 function, names = self._get_fields(line) 506 self.importer.function_parameters[function] = \ 507 self.function_parameters[function] = names != "{}" and names.split(", ") or [] 508 line = f.readline().rstrip() 509 510 def _get_function_defaults(self, f): 511 f.readline() # "function default parameters:" 512 line = f.readline().rstrip() 513 while line: 514 function, defaults = self._get_fields(line) 515 self.importer.function_defaults[function] = \ 516 self.function_defaults[function] = l = [] 517 if defaults != "{}": 518 for value in defaults.split(", "): 519 name, default = value.split("=") 520 default = decode_reference(default) 521 l.append((name, default)) 522 line = f.readline().rstrip() 523 524 def _get_function_locals(self, f): 525 f.readline() # "function locals:" 526 line = f.readline().rstrip() 527 while line: 528 function, name, value = self._get_fields(line, 3) 529 init_item(self.function_locals, function, dict) 530 init_item(self.importer.function_locals, function, dict) 531 if name != "{}": 532 self.importer.function_locals[function][name] = \ 533 self.function_locals[function][name] = decode_reference(value) 534 line = f.readline().rstrip() 535 536 def _get_function_targets(self, f): 537 f.readline() # "function targets:" 538 line = f.readline().rstrip() 539 while line: 540 function, n = self._get_fields(line) 541 self.importer.function_targets[function] = \ 542 self.function_targets[function] = int(n) 543 line = f.readline().rstrip() 544 545 def _get_function_arguments(self, f): 546 f.readline() # "function arguments:" 547 line = f.readline().rstrip() 548 while line: 549 function, n = self._get_fields(line) 550 self.importer.function_arguments[function] = \ 551 self.function_arguments[function] = int(n) 552 line = f.readline().rstrip() 553 554 def _get_attribute_usage(self, f): 555 f.readline() # "attribute usage:" 556 line = f.readline().rstrip() 557 while line: 558 unit, value = self._get_fields(line) 559 init_item(self.attr_usage, unit, dict) 560 self.usage_from_cache(value, self.attr_usage[unit]) 561 line = f.readline().rstrip() 562 563 def _get_attr_accesses(self, f): 564 f.readline() # "attribute accesses:" 565 line = f.readline().rstrip() 566 while line: 567 name, value = self._get_fields(line) 568 self.attr_accesses[name] = set(value.split(", ")) 569 line = f.readline().rstrip() 570 571 def _get_const_accesses(self, f): 572 f.readline() # "constant accesses:" 573 line = f.readline().rstrip() 574 while line: 575 name, original_name, attrnames, objpath, ref, remaining = self._get_fields(line, 6) 576 if attrnames == "{}": attrnames = None 577 init_item(self.const_accesses, name, dict) 578 self.const_accesses[name][(original_name, attrnames)] = (objpath, decode_reference(ref), remaining != "{}" and remaining or "") 579 line = f.readline().rstrip() 580 581 def _get_attr_accessors(self, f): 582 f.readline() # "attribute access usage:" 583 line = f.readline().rstrip() 584 while line: 585 objpath, name, attrname, value = self._get_fields(line, 4) 586 if attrname == "{}": attrname = None 587 access = name, attrname 588 init_item(self.attr_accessors, objpath, dict) 589 init_item(self.attr_accessors[objpath], access, list) 590 positions = map(int, value.split(", ")) 591 self.attr_accessors[objpath][access].append(positions) 592 line = f.readline().rstrip() 593 594 def _get_attr_access_modifiers(self, f): 595 f.readline() # "attribute access modifiers:" 596 line = f.readline().rstrip() 597 while line: 598 objpath, name, attrnames, value = self._get_fields(line, 4) 599 if name == "{}": name = None 600 if attrnames == "{}": attrnames = None 601 access = name, attrnames 602 init_item(self.attr_access_modifiers, objpath, dict) 603 init_item(self.attr_access_modifiers[objpath], access, list) 604 modifiers = [decode_modifier_term(s) for s in value] 605 self.attr_access_modifiers[objpath][access] = modifiers 606 line = f.readline().rstrip() 607 608 def _get_constant_literals(self, f): 609 f.readline() # "constant literals:" 610 line = f.readline().rstrip() 611 last_path = None 612 n = None 613 while line: 614 path, value_type, encoding, value = self._get_fields(line, 4) 615 if path != last_path: 616 n = 0 617 last_path = path 618 else: 619 n += 1 620 init_item(self.constants, path, dict) 621 value = eval(value) 622 encoding = encoding != "{}" and encoding or None 623 self.constants[path][(value, value_type, encoding)] = n 624 line = f.readline().rstrip() 625 626 def _get_constant_values(self, f): 627 f.readline() # "constant values:" 628 line = f.readline().rstrip() 629 while line: 630 name, value_type, encoding, value = self._get_fields(line, 4) 631 value = eval(value) 632 encoding = encoding != "{}" and encoding or None 633 self.constant_values[name] = value, value_type, encoding 634 line = f.readline().rstrip() 635 636 # Generic parsing methods. 637 638 def from_lines(self, f, d): 639 640 "Read lines from 'f', populating 'd'." 641 642 f.readline() # section heading 643 line = f.readline().rstrip() 644 while line: 645 name, value = self._get_fields(line) 646 d[name] = set(value and value.split(", ") or []) 647 line = f.readline().rstrip() 648 649 def usage_from_cache(self, value, mapping): 650 651 """ 652 Interpret the given 'value' containing name and usage information, 653 storing the information in the given 'mapping'. 654 """ 655 656 local, usage = self._get_fields(value) 657 init_item(mapping, local, list) 658 self._usage_from_cache(mapping[local], usage) 659 660 def _usage_from_cache(self, d, usage): 661 662 # Interpret descriptions of each version of the name. 663 664 all_usages = set() 665 for attrnames in usage.split("; "): 666 if attrnames == "{}": 667 all_attrnames = () 668 else: 669 all_attrnames = decode_usage(attrnames) 670 all_usages.add(all_attrnames) 671 672 d.append(all_usages) 673 674 def _get_fields(self, s, n=2): 675 result = s.split(" ", n-1) 676 if len(result) == n: 677 return result 678 else: 679 return tuple(result) + tuple([""] * (n - len(result))) 680 681 class CacheWritingModule: 682 683 """ 684 A mix-in providing cache-writing support, to be combined with BasicModule. 685 """ 686 687 def to_cache(self, filename): 688 689 """ 690 Write a cached representation of the inspected module with the following 691 format to the file having the given 'filename': 692 693 filename 694 (empty line) 695 "imports:" 696 required module names 697 possibly required module names 698 "deferred:" 699 deferred references 700 "special:" 701 zero or more: special name " " reference " " qualified names 702 (empty line) 703 "members:" 704 zero or more: qualified name " " reference 705 (empty line) 706 "class relationships:" 707 zero or more: qualified class name " " base class references 708 (empty line) 709 "instance attributes:" 710 zero or more: qualified class name " " instance attribute names 711 (empty line) 712 "instance attribute constants:" 713 zero or more: qualified class name " " attribute name " " reference 714 (empty line) 715 "names used:" 716 zero or more: qualified class/function/module name " " names 717 (empty line) 718 "names missing:" 719 zero or more: qualified class/function/module name " " names 720 (empty line) 721 "name references:" 722 zero or more: qualified name " " reference 723 (empty line) 724 "initialised names:" 725 zero or more: qualified name " " definition version " " reference 726 (empty line) 727 "aliased names:" 728 zero or more: qualified name " " definition version " " original name " " attribute names " " access number 729 (empty line) 730 "function parameters:" 731 zero or more: qualified function name " " parameter names 732 (empty line) 733 "function default parameters:" 734 zero or more: qualified function name " " parameter names with defaults 735 (empty line) 736 "function locals:" 737 zero or more: qualified function name " " local variable name " " reference 738 (empty line) 739 "scope globals:" 740 zero or more: qualified function name " " global variable names 741 (empty line) 742 "function targets:" 743 zero or more: qualified function name " " maximum number of targets allocated 744 (empty line) 745 "function arguments:" 746 zero or more: qualified function name " " maximum number of arguments allocated 747 (empty line) 748 "attribute usage:" 749 zero or more: qualified scope name " " local/global/qualified variable name " " usages 750 (empty line) 751 "attribute accesses:" 752 zero or more: qualified scope name " " attribute-chains 753 (empty line) 754 "constant accesses:" 755 zero or more: qualified function name " " attribute-chain " " reference " " remaining attribute-chain 756 (empty line) 757 "attribute access usage:" 758 zero or more: qualified function name " " local/global variable name " " attribute name " " definition versions 759 (empty line) 760 "attribute access modifiers:" 761 zero or more: qualified function name " " local/global variable name " " attribute name " " access modifiers 762 "constant literals:" 763 zero or more: qualified scope name " " value type " " constant literal 764 "constant values:" 765 zero or more: qualified name " " value type " " constant literal 766 767 All collections of names are separated by ", " characters. 768 769 References can be "<var>", a module name, or one of "<class>" or 770 "<function>" followed optionally by a ":" character and a qualified 771 name. 772 773 Parameter names with defaults are separated by ", " characters, with 774 each name followed by "=" and then followed by a reference. If "{}" is 775 indicated, no defaults are defined for the function. Similarly, function 776 locals may be indicated as "{}" meaning that there are no locals. 777 778 All usages (attribute usage sets) are separated by "; " characters, with 779 the special string "{}" representing an empty set. 780 781 Each usage is a collection of names separated by ", " characters, with 782 invoked attribute names suffixed with a "!" character. 783 784 Definition versions are separated by ", " characters and indicate the 785 name definition version associated with the access. 786 787 Access modifiers are separated by ", " characters and indicate features 788 of each access, with multiple accesses described on a single line. 789 """ 790 791 f = open(filename, "w") 792 try: 793 print >>f, self.filename 794 795 print >>f 796 print >>f, "imports:" 797 required = list(self.required) 798 required.sort() 799 print >>f, required and ", ".join(required) or "{}" 800 imports = list(self.imports) 801 imports.sort() 802 print >>f, imports and ", ".join(imports) or "{}" 803 804 print >>f 805 print >>f, "deferred:" 806 deferred = map(str, set(self.deferred)) 807 deferred.sort() 808 print >>f, " ".join(deferred) 809 810 print >>f 811 print >>f, "special:" 812 names = self.special.keys() 813 names.sort() 814 for name in names: 815 ref, paths = self.special[name] 816 print >>f, name, ref, ", ".join(paths) 817 818 print >>f 819 print >>f, "members:" 820 objects = self.objects.keys() 821 objects.sort() 822 for name in objects: 823 print >>f, name, self.objects[name] 824 825 print >>f 826 print >>f, "class relationships:" 827 classes = self.classes.keys() 828 classes.sort() 829 for class_ in classes: 830 bases = self.classes[class_] 831 if bases: 832 print >>f, class_, ", ".join(map(str, bases)) 833 else: 834 print >>f, class_ 835 836 self.to_lines(f, "instance attributes:", self.instance_attrs) 837 838 print >>f 839 print >>f, "instance attribute constants:" 840 classes = self.instance_attr_constants.items() 841 classes.sort() 842 for name, attrs in classes: 843 attrs = attrs.items() 844 attrs.sort() 845 for attrname, ref in attrs: 846 print >>f, name, attrname, ref 847 848 self.to_lines(f, "names used:", self.names_used) 849 850 print >>f 851 print >>f, "name references:" 852 refs = self.name_references.items() 853 refs.sort() 854 for name, ref in refs: 855 print >>f, name, ref 856 857 print >>f 858 print >>f, "initialised names:" 859 assignments = self.initialised_names.items() 860 assignments.sort() 861 for name, refs in assignments: 862 versions = refs.items() 863 versions.sort() 864 for version, ref in versions: 865 print >>f, name, version, ref 866 867 print >>f 868 print >>f, "aliased names:" 869 assignments = self.aliased_names.items() 870 assignments.sort() 871 for name, aliases in assignments: 872 versions = aliases.items() 873 versions.sort() 874 for version, alias in versions: 875 original_name, attrnames, number = alias 876 print >>f, name, version, original_name, attrnames or "{}", number is None and "{}" or number 877 878 print >>f 879 print >>f, "function parameters:" 880 functions = self.function_parameters.keys() 881 functions.sort() 882 for function in functions: 883 parameters = self.function_parameters[function] 884 if parameters: 885 print >>f, function, ", ".join(parameters) 886 else: 887 print >>f, function, "{}" 888 889 print >>f 890 print >>f, "function default parameters:" 891 functions = self.function_defaults.keys() 892 functions.sort() 893 for function in functions: 894 parameters = self.function_defaults[function] 895 if parameters: 896 print >>f, function, ", ".join([("%s=%s" % (name, default)) for (name, default) in parameters]) 897 else: 898 print >>f, function, "{}" 899 900 print >>f 901 print >>f, "function locals:" 902 functions = self.function_locals.keys() 903 functions.sort() 904 for function in functions: 905 names = self.function_locals[function].items() 906 if names: 907 names.sort() 908 for name, value in names: 909 print >>f, function, name, value 910 else: 911 print >>f, function, "{}" 912 913 self.to_lines(f, "scope globals:", self.scope_globals) 914 915 print >>f 916 print >>f, "function targets:" 917 functions = self.function_targets.keys() 918 functions.sort() 919 for function in functions: 920 print >>f, function, self.function_targets[function] 921 922 print >>f 923 print >>f, "function arguments:" 924 functions = self.function_arguments.keys() 925 functions.sort() 926 for function in functions: 927 print >>f, function, self.function_arguments[function] 928 929 print >>f 930 print >>f, "attribute usage:" 931 units = self.attr_usage.keys() 932 units.sort() 933 for unit in units: 934 d = self.attr_usage[unit] 935 self.usage_to_cache(d, f, unit) 936 937 print >>f 938 print >>f, "attribute accesses:" 939 paths = self.attr_accesses.keys() 940 paths.sort() 941 for path in paths: 942 accesses = list(self.attr_accesses[path]) 943 accesses.sort() 944 print >>f, path, ", ".join(accesses) 945 946 print >>f 947 print >>f, "constant accesses:" 948 paths = self.const_accesses.keys() 949 paths.sort() 950 for path in paths: 951 accesses = self.const_accesses[path].items() 952 accesses.sort() 953 for (original_name, attrnames), (objpath, ref, remaining_attrnames) in accesses: 954 print >>f, path, original_name, attrnames, objpath, ref, remaining_attrnames or "{}" 955 956 print >>f 957 print >>f, "attribute access usage:" 958 paths = self.attr_accessors.keys() 959 paths.sort() 960 for path in paths: 961 all_accesses = self.attr_accessors[path].items() 962 all_accesses.sort() 963 for (name, attrname), accesses in all_accesses: 964 for positions in accesses: 965 positions = map(str, positions) 966 print >>f, path, name, attrname or "{}", ", ".join(positions) 967 968 print >>f 969 print >>f, "attribute access modifiers:" 970 paths = self.attr_access_modifiers.keys() 971 paths.sort() 972 for path in paths: 973 all_accesses = self.attr_access_modifiers[path].items() 974 all_accesses.sort() 975 for (name, attrnames), modifiers in all_accesses: 976 print >>f, path, name or "{}", attrnames or "{}", encode_modifiers(modifiers) 977 978 print >>f 979 print >>f, "constant literals:" 980 paths = self.constants.keys() 981 paths.sort() 982 for path in paths: 983 constants = [] 984 for (value, value_type, encoding), n in self.constants[path].items(): 985 constants.append((n, value_type, encoding, value)) 986 constants.sort() 987 for n, value_type, encoding, value in constants: 988 print >>f, path, value_type, encoding or "{}", repr(value) 989 990 print >>f 991 print >>f, "constant values:" 992 names = self.constant_values.keys() 993 names.sort() 994 for name in names: 995 value, value_type, encoding = self.constant_values[name] 996 print >>f, name, value_type, encoding or "{}", repr(value) 997 998 finally: 999 f.close() 1000 1001 def to_lines(self, f, heading, d): 1002 1003 "Write lines to 'f' with the given 'heading', using 'd'." 1004 1005 print >>f 1006 print >>f, heading 1007 keys = d.keys() 1008 keys.sort() 1009 for key in keys: 1010 attrs = list(d[key]) 1011 if attrs: 1012 attrs.sort() 1013 print >>f, key, ", ".join(attrs) 1014 1015 def usage_to_cache(self, details, f, prefix): 1016 1017 "Write the given namespace usage details to the cache." 1018 1019 names = list(details.keys()) 1020 if names: 1021 names.sort() 1022 for name in names: 1023 if details[name]: 1024 1025 # Produce descriptions for each version of the name. 1026 1027 for version in details[name]: 1028 all_usages = [] 1029 for usage in version: 1030 all_usages.append(encode_usage(usage)) 1031 1032 print >>f, "%s %s %s" % (prefix, name, "; ".join(all_usages)) 1033 1034 # vim: tabstop=4 expandtab shiftwidth=4