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