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