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