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