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