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