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