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