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