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 = line.split(" ", 1) 435 self.special[name] = decode_reference(ref) 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, value = self._get_fields(line, 3) 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 self.constants[path][(value, value_type)] = n 623 line = f.readline().rstrip() 624 625 def _get_constant_values(self, f): 626 f.readline() # "constant values:" 627 line = f.readline().rstrip() 628 while line: 629 name, value_type, value = self._get_fields(line, 3) 630 self.constant_values[name] = eval(value), value_type 631 line = f.readline().rstrip() 632 633 # Generic parsing methods. 634 635 def from_lines(self, f, d): 636 637 "Read lines from 'f', populating 'd'." 638 639 f.readline() # section heading 640 line = f.readline().rstrip() 641 while line: 642 name, value = self._get_fields(line) 643 d[name] = set(value and value.split(", ") or []) 644 line = f.readline().rstrip() 645 646 def usage_from_cache(self, value, mapping): 647 648 """ 649 Interpret the given 'value' containing name and usage information, 650 storing the information in the given 'mapping'. 651 """ 652 653 local, usage = self._get_fields(value) 654 init_item(mapping, local, list) 655 self._usage_from_cache(mapping[local], usage) 656 657 def _usage_from_cache(self, d, usage): 658 659 # Interpret descriptions of each version of the name. 660 661 all_usages = set() 662 for attrnames in usage.split("; "): 663 if attrnames == "{}": 664 all_attrnames = () 665 else: 666 all_attrnames = decode_usage(attrnames) 667 all_usages.add(all_attrnames) 668 669 d.append(all_usages) 670 671 def _get_fields(self, s, n=2): 672 result = s.split(" ", n-1) 673 if len(result) == n: 674 return result 675 else: 676 return tuple(result) + tuple([""] * (n - len(result))) 677 678 class CacheWritingModule: 679 680 """ 681 A mix-in providing cache-writing support, to be combined with BasicModule. 682 """ 683 684 def to_cache(self, filename): 685 686 """ 687 Write a cached representation of the inspected module with the following 688 format to the file having the given 'filename': 689 690 filename 691 (empty line) 692 "imports:" 693 required module names 694 possibly required module names 695 "deferred:" 696 deferred references 697 "special:" 698 zero or more: special name " " reference 699 (empty line) 700 "members:" 701 zero or more: qualified name " " reference 702 (empty line) 703 "class relationships:" 704 zero or more: qualified class name " " base class references 705 (empty line) 706 "instance attributes:" 707 zero or more: qualified class name " " instance attribute names 708 (empty line) 709 "instance attribute constants:" 710 zero or more: qualified class name " " attribute name " " reference 711 (empty line) 712 "names used:" 713 zero or more: qualified class/function/module name " " names 714 (empty line) 715 "names missing:" 716 zero or more: qualified class/function/module name " " names 717 (empty line) 718 "name references:" 719 zero or more: qualified name " " reference 720 (empty line) 721 "initialised names:" 722 zero or more: qualified name " " definition version " " reference 723 (empty line) 724 "aliased names:" 725 zero or more: qualified name " " definition version " " original name " " attribute names " " access number 726 (empty line) 727 "function parameters:" 728 zero or more: qualified function name " " parameter names 729 (empty line) 730 "function default parameters:" 731 zero or more: qualified function name " " parameter names with defaults 732 (empty line) 733 "function locals:" 734 zero or more: qualified function name " " local variable name " " reference 735 (empty line) 736 "scope globals:" 737 zero or more: qualified function name " " global variable names 738 (empty line) 739 "function targets:" 740 zero or more: qualified function name " " maximum number of targets allocated 741 (empty line) 742 "function arguments:" 743 zero or more: qualified function name " " maximum number of arguments allocated 744 (empty line) 745 "attribute usage:" 746 zero or more: qualified scope name " " local/global/qualified variable name " " usages 747 (empty line) 748 "attribute accesses:" 749 zero or more: qualified scope name " " attribute-chains 750 (empty line) 751 "constant accesses:" 752 zero or more: qualified function name " " attribute-chain " " reference " " remaining attribute-chain 753 (empty line) 754 "attribute access usage:" 755 zero or more: qualified function name " " local/global variable name " " attribute name " " definition versions 756 (empty line) 757 "attribute access modifiers:" 758 zero or more: qualified function name " " local/global variable name " " attribute name " " access modifiers 759 "constant literals:" 760 zero or more: qualified scope name " " value type " " constant literal 761 "constant values:" 762 zero or more: qualified name " " value type " " constant literal 763 764 All collections of names are separated by ", " characters. 765 766 References can be "<var>", a module name, or one of "<class>" or 767 "<function>" followed optionally by a ":" character and a qualified 768 name. 769 770 Parameter names with defaults are separated by ", " characters, with 771 each name followed by "=" and then followed by a reference. If "{}" is 772 indicated, no defaults are defined for the function. Similarly, function 773 locals may be indicated as "{}" meaning that there are no locals. 774 775 All usages (attribute usage sets) are separated by "; " characters, with 776 the special string "{}" representing an empty set. 777 778 Each usage is a collection of names separated by ", " characters, with 779 invoked attribute names suffixed with a "!" character. 780 781 Definition versions are separated by ", " characters and indicate the 782 name definition version associated with the access. 783 784 Access modifiers are separated by ", " characters and indicate features 785 of each access, with multiple accesses described on a single line. 786 """ 787 788 f = open(filename, "w") 789 try: 790 print >>f, self.filename 791 792 print >>f 793 print >>f, "imports:" 794 required = list(self.required) 795 required.sort() 796 print >>f, required and ", ".join(required) or "{}" 797 imports = list(self.imports) 798 imports.sort() 799 print >>f, imports and ", ".join(imports) or "{}" 800 801 print >>f 802 print >>f, "deferred:" 803 print >>f, " ".join(map(str, self.deferred)) 804 805 print >>f 806 print >>f, "special:" 807 names = self.special.keys() 808 names.sort() 809 for name in names: 810 print >>f, name, self.special[name] 811 812 print >>f 813 print >>f, "members:" 814 objects = self.objects.keys() 815 objects.sort() 816 for name in objects: 817 print >>f, name, self.objects[name] 818 819 print >>f 820 print >>f, "class relationships:" 821 classes = self.classes.keys() 822 classes.sort() 823 for class_ in classes: 824 bases = self.classes[class_] 825 if bases: 826 print >>f, class_, ", ".join(map(str, bases)) 827 else: 828 print >>f, class_ 829 830 self.to_lines(f, "instance attributes:", self.instance_attrs) 831 832 print >>f 833 print >>f, "instance attribute constants:" 834 classes = self.instance_attr_constants.items() 835 classes.sort() 836 for name, attrs in classes: 837 attrs = attrs.items() 838 attrs.sort() 839 for attrname, ref in attrs: 840 print >>f, name, attrname, ref 841 842 self.to_lines(f, "names used:", self.names_used) 843 844 print >>f 845 print >>f, "name references:" 846 refs = self.name_references.items() 847 refs.sort() 848 for name, ref in refs: 849 print >>f, name, ref 850 851 print >>f 852 print >>f, "initialised names:" 853 assignments = self.initialised_names.items() 854 assignments.sort() 855 for name, refs in assignments: 856 versions = refs.items() 857 versions.sort() 858 for version, ref in versions: 859 print >>f, name, version, ref 860 861 print >>f 862 print >>f, "aliased names:" 863 assignments = self.aliased_names.items() 864 assignments.sort() 865 for name, aliases in assignments: 866 versions = aliases.items() 867 versions.sort() 868 for version, alias in versions: 869 original_name, attrnames, number = alias 870 print >>f, name, version, original_name, attrnames or "{}", number is None and "{}" or number 871 872 print >>f 873 print >>f, "function parameters:" 874 functions = self.function_parameters.keys() 875 functions.sort() 876 for function in functions: 877 parameters = self.function_parameters[function] 878 if parameters: 879 print >>f, function, ", ".join(parameters) 880 else: 881 print >>f, function, "{}" 882 883 print >>f 884 print >>f, "function default parameters:" 885 functions = self.function_defaults.keys() 886 functions.sort() 887 for function in functions: 888 parameters = self.function_defaults[function] 889 if parameters: 890 print >>f, function, ", ".join([("%s=%s" % (name, default)) for (name, default) in parameters]) 891 else: 892 print >>f, function, "{}" 893 894 print >>f 895 print >>f, "function locals:" 896 functions = self.function_locals.keys() 897 functions.sort() 898 for function in functions: 899 names = self.function_locals[function].items() 900 if names: 901 names.sort() 902 for name, value in names: 903 print >>f, function, name, value 904 else: 905 print >>f, function, "{}" 906 907 self.to_lines(f, "scope globals:", self.scope_globals) 908 909 print >>f 910 print >>f, "function targets:" 911 functions = self.function_targets.keys() 912 functions.sort() 913 for function in functions: 914 print >>f, function, self.function_targets[function] 915 916 print >>f 917 print >>f, "function arguments:" 918 functions = self.function_arguments.keys() 919 functions.sort() 920 for function in functions: 921 print >>f, function, self.function_arguments[function] 922 923 print >>f 924 print >>f, "attribute usage:" 925 units = self.attr_usage.keys() 926 units.sort() 927 for unit in units: 928 d = self.attr_usage[unit] 929 self.usage_to_cache(d, f, unit) 930 931 print >>f 932 print >>f, "attribute accesses:" 933 paths = self.attr_accesses.keys() 934 paths.sort() 935 for path in paths: 936 accesses = list(self.attr_accesses[path]) 937 accesses.sort() 938 print >>f, path, ", ".join(accesses) 939 940 print >>f 941 print >>f, "constant accesses:" 942 paths = self.const_accesses.keys() 943 paths.sort() 944 for path in paths: 945 accesses = self.const_accesses[path].items() 946 accesses.sort() 947 for (original_name, attrnames), (objpath, ref, remaining_attrnames) in accesses: 948 print >>f, path, original_name, attrnames, objpath, ref, remaining_attrnames or "{}" 949 950 print >>f 951 print >>f, "attribute access usage:" 952 paths = self.attr_accessors.keys() 953 paths.sort() 954 for path in paths: 955 all_accesses = self.attr_accessors[path].items() 956 all_accesses.sort() 957 for (name, attrname), accesses in all_accesses: 958 for positions in accesses: 959 positions = map(str, positions) 960 print >>f, path, name, attrname or "{}", ", ".join(positions) 961 962 print >>f 963 print >>f, "attribute access modifiers:" 964 paths = self.attr_access_modifiers.keys() 965 paths.sort() 966 for path in paths: 967 all_accesses = self.attr_access_modifiers[path].items() 968 all_accesses.sort() 969 for (name, attrnames), modifiers in all_accesses: 970 print >>f, path, name or "{}", attrnames or "{}", encode_modifiers(modifiers) 971 972 print >>f 973 print >>f, "constant literals:" 974 paths = self.constants.keys() 975 paths.sort() 976 for path in paths: 977 constants = [] 978 for (value, value_type), n in self.constants[path].items(): 979 constants.append((n, value_type, value)) 980 constants.sort() 981 for n, value_type, value in constants: 982 print >>f, path, value_type, repr(value) 983 984 print >>f 985 print >>f, "constant values:" 986 names = self.constant_values.keys() 987 names.sort() 988 for name in names: 989 value, value_type = self.constant_values[name] 990 print >>f, name, value_type, repr(value) 991 992 finally: 993 f.close() 994 995 def to_lines(self, f, heading, d): 996 997 "Write lines to 'f' with the given 'heading', using 'd'." 998 999 print >>f 1000 print >>f, heading 1001 keys = d.keys() 1002 keys.sort() 1003 for key in keys: 1004 attrs = list(d[key]) 1005 if attrs: 1006 attrs.sort() 1007 print >>f, key, ", ".join(attrs) 1008 1009 def usage_to_cache(self, details, f, prefix): 1010 1011 "Write the given namespace usage details to the cache." 1012 1013 names = list(details.keys()) 1014 if names: 1015 names.sort() 1016 for name in names: 1017 if details[name]: 1018 1019 # Produce descriptions for each version of the name. 1020 1021 for version in details[name]: 1022 all_usages = [] 1023 for usage in version: 1024 all_usages.append(encode_usage(usage)) 1025 1026 print >>f, "%s %s %s" % (prefix, name, "; ".join(all_usages)) 1027 1028 # vim: tabstop=4 expandtab shiftwidth=4