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