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, 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 41 # Global name information. 42 43 self.objects = {} 44 self.special = {} 45 46 # Class relationships. 47 48 self.classes = {} 49 50 # Attributes. 51 52 self.class_attrs = {} 53 self.instance_attrs = {} 54 self.instance_attr_constants = {} 55 self.module_attrs = set() 56 57 # Names used and missing. 58 59 self.names_used = {} 60 self.names_missing = {} 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.all_class_attrs[name] 126 del self.importer.all_instance_attrs[name] 127 del self.importer.all_instance_attr_constants[name] 128 129 for name, bases in self.classes.items(): 130 for base in bases: 131 132 # Get the identity of the class from the reference. 133 134 base = base.get_origin() 135 136 try: 137 self.importer.subclasses[base].remove(name) 138 except (KeyError, ValueError): 139 pass 140 141 remove_items(self.importer.all_name_references, self.name_references) 142 remove_items(self.importer.all_initialised_names, self.initialised_names) 143 remove_items(self.importer.all_aliased_names, self.aliased_names) 144 remove_items(self.importer.all_attr_accesses, self.attr_accesses) 145 remove_items(self.importer.all_const_accesses, self.const_accesses) 146 remove_items(self.importer.all_attr_access_modifiers, self.attr_access_modifiers) 147 remove_items(self.importer.all_constants, self.constants) 148 remove_items(self.importer.all_constant_values, self.constant_values) 149 150 # Remove this module's objects from the importer. Objects are 151 # automatically propagated when defined. 152 153 for name, ref in self.objects.items(): 154 del self.importer.objects[name] 155 156 def propagate_attrs(self): 157 158 "Derive attributes from the class and module member details." 159 160 # Initialise class attribute records for all classes. 161 162 for name in self.classes.keys(): 163 self.importer.all_class_attrs[name] = self.class_attrs[name] = {} 164 165 # Separate the objects into module and class attributes. 166 167 for name in self.objects.keys(): 168 if "." in name: 169 parent, attrname = name.rsplit(".", 1) 170 if self.classes.has_key(parent): 171 self.class_attrs[parent][attrname] = name 172 elif parent == self.name: 173 self.module_attrs.add(attrname) 174 175 # Propagate the module attributes. 176 177 self.importer.all_module_attrs[self.name] = self.module_attrs 178 179 def propagate_name_references(self): 180 181 "Propagate name references for the module." 182 183 self.importer.all_name_references.update(self.name_references) 184 self.importer.all_initialised_names.update(self.initialised_names) 185 self.importer.all_aliased_names.update(self.aliased_names) 186 187 def propagate_attr_accesses(self): 188 189 "Propagate attribute accesses for the module." 190 191 self.importer.all_attr_accesses.update(self.attr_accesses) 192 self.importer.all_const_accesses.update(self.const_accesses) 193 self.importer.all_attr_access_modifiers.update(self.attr_access_modifiers) 194 195 def propagate_constants(self): 196 197 "Propagate constant values and aliases for the module." 198 199 self.importer.all_constants.update(self.constants) 200 self.importer.all_constant_values.update(self.constant_values) 201 202 for name in self.classes.keys(): 203 self.importer.all_instance_attrs[name] = self.instance_attrs.get(name) or {} 204 self.importer.all_instance_attr_constants[name] = self.instance_attr_constants.get(name) or {} 205 206 # Module-relative naming. 207 208 def is_global(self, name): 209 210 """ 211 Return whether 'name' is registered as a global in the current 212 namespace. 213 """ 214 215 path = self.get_namespace_path() 216 return name in self.scope_globals.get(path, []) 217 218 def get_global(self, name): 219 220 """ 221 Get the global of the given 'name' from this module, returning a 222 reference incorporating the original definition details. 223 """ 224 225 path = self.get_global_path(name) 226 return self.objects.get(path) 227 228 # Name definition discovery. 229 230 def get_global_or_builtin(self, name): 231 232 """ 233 Return a reference for the given 'name' found in this module or in the 234 __builtins__. 235 """ 236 237 return self.get_global(name) or self.get_builtin(name) 238 239 def get_builtin(self, name): 240 241 "Return a reference to the built-in with the given 'name'." 242 243 self.queue_module("__builtins__") 244 return Reference("<depends>", "__builtins__.%s" % name) 245 246 def get_builtin_class(self, name): 247 248 "Return a reference to the actual object providing 'name'." 249 250 # NOTE: This makes assumptions about the __builtins__ structure. 251 252 return Reference("<class>", "__builtins__.%s.%s" % (name, name)) 253 254 def get_object(self, path): 255 256 """ 257 Get the details of an object with the given 'path'. Where the object 258 cannot be resolved, an unresolved reference is returned. 259 """ 260 261 if self.objects.has_key(path): 262 return self.objects[path] 263 else: 264 return Reference("<depends>", path) 265 266 def set_object(self, name, value=None): 267 268 "Set an object with the given 'name' and the given 'value'." 269 270 ref = decode_reference(value, name) 271 multiple = self.objects.has_key(name) and self.objects[name].get_kind() != ref.get_kind() 272 self.importer.objects[name] = self.objects[name] = multiple and ref.as_var() or ref 273 274 def import_name_from_module(self, name, module_name): 275 276 "Import 'name' from the module having the given 'module_name'." 277 278 if module_name != self.name: 279 self.queue_module(module_name) 280 return Reference("<depends>", "%s.%s" % (module_name, name)) 281 282 def queue_module(self, name, required=False): 283 284 """ 285 Queue the module with the given 'name'. If 'required' is true (it is 286 false by default), indicate that the module is required in the final 287 program. 288 """ 289 290 self.importer.queue_module(name, self, required) 291 if required: 292 self.required.add(name) 293 self.imports.add(name) 294 295 class CachedModule(BasicModule): 296 297 "A cached module." 298 299 def __repr__(self): 300 return "CachedModule(%r, %r)" % (self.name, self.importer) 301 302 def to_cache(self, filename): 303 304 "Not actually writing the module back to 'filename'." 305 306 pass 307 308 def from_cache(self, filename): 309 310 """ 311 Read a module's details from the file with the given 'filename' as 312 described in the to_cache method of InspectedModule. 313 """ 314 315 f = open(filename) 316 try: 317 self.filename = f.readline().rstrip() 318 319 f.readline() # (empty line) 320 321 self._get_imports(f) 322 self._get_members(f) 323 self._get_class_relationships(f) 324 self._get_instance_attrs(f) 325 self._get_instance_attr_constants(f) 326 self.from_lines(f, self.names_used) # "names used:" 327 self.from_lines(f, self.names_missing) # "names missing:" 328 self._get_name_references(f) 329 self._get_initialised_names(f) 330 self._get_aliased_names(f) 331 self._get_function_parameters(f) 332 self._get_function_defaults(f) 333 self._get_function_locals(f) 334 self.from_lines(f, self.scope_globals) # "scope globals:" 335 self._get_function_targets(f) 336 self._get_function_arguments(f) 337 self._get_attribute_usage(f) 338 self._get_attr_accesses(f) 339 self._get_const_accesses(f) 340 self._get_attr_accessors(f) 341 self._get_attr_access_modifiers(f) 342 self._get_constant_literals(f) 343 self._get_constant_values(f) 344 345 finally: 346 f.close() 347 348 def complete(self): 349 self.propagate() 350 351 def _get_imports(self, f): 352 f.readline() # "imports:" 353 line = f.readline().strip() 354 self.required = line != "{}" and set(line.split(", ")) or set() 355 line = f.readline().strip() 356 self.imports = line != "{}" and set(line.split(", ")) or set() 357 f.readline() 358 359 for name in self.required: 360 self.queue_module(name, True) 361 for name in self.imports: 362 self.queue_module(name) 363 364 def _get_members(self, f): 365 f.readline() # "members:" 366 line = f.readline().rstrip() 367 while line: 368 name, ref = line.split(" ", 1) 369 self.set_object(name, ref) 370 line = f.readline().rstrip() 371 372 def _get_class_relationships(self, f): 373 f.readline() # "class relationships:" 374 line = f.readline().rstrip() 375 while line: 376 name, value = self._get_fields(line) 377 values = value and value.split(", ") or [] 378 self.importer.classes[name] = self.classes[name] = map(decode_reference, values) 379 self.importer.subclasses[name] = set() 380 line = f.readline().rstrip() 381 382 def _get_instance_attrs(self, f): 383 f.readline() # "instance attributes:" 384 line = f.readline().rstrip() 385 while line: 386 name, value = self._get_fields(line) 387 self.importer.all_instance_attrs[name] = self.instance_attrs[name] = set(value and value.split(", ") or []) 388 line = f.readline().rstrip() 389 390 def _get_instance_attr_constants(self, f): 391 f.readline() # "instance attribute constants:" 392 line = f.readline().rstrip() 393 while line: 394 name, attrname, ref = self._get_fields(line, 3) 395 init_item(self.instance_attr_constants, name, dict) 396 self.instance_attr_constants[name][attrname] = decode_reference(ref) 397 line = f.readline().rstrip() 398 399 def _get_name_references(self, f): 400 f.readline() # "name references:" 401 line = f.readline().rstrip() 402 while line: 403 name, value = self._get_fields(line) 404 self.name_references[name] = value 405 line = f.readline().rstrip() 406 407 def _get_initialised_names(self, f): 408 f.readline() # "initialised names:" 409 line = f.readline().rstrip() 410 while line: 411 name, version, value = self._get_fields(line, 3) 412 init_item(self.initialised_names, name, dict) 413 self.initialised_names[name][int(version)] = decode_reference(value) 414 line = f.readline().rstrip() 415 416 def _get_aliased_names(self, f): 417 f.readline() # "aliased names:" 418 line = f.readline().rstrip() 419 while line: 420 name, version, original_name, attrnames, number = self._get_fields(line, 5) 421 init_item(self.aliased_names, name, dict) 422 if number == "{}": number = None 423 else: number = int(number) 424 self.aliased_names[name][int(version)] = (original_name, attrnames != "{}" and attrnames or None, number) 425 line = f.readline().rstrip() 426 427 def _get_function_parameters(self, f): 428 f.readline() # "function parameters:" 429 line = f.readline().rstrip() 430 while line: 431 function, names = self._get_fields(line) 432 self.importer.function_parameters[function] = \ 433 self.function_parameters[function] = names and names.split(", ") or [] 434 line = f.readline().rstrip() 435 436 def _get_function_defaults(self, f): 437 f.readline() # "function default parameters:" 438 line = f.readline().rstrip() 439 while line: 440 function, defaults = self._get_fields(line) 441 self.importer.function_defaults[function] = \ 442 self.function_defaults[function] = l = [] 443 if defaults != "{}": 444 for value in defaults.split(", "): 445 name, default = value.split("=") 446 default = decode_reference(default) 447 l.append((name, default)) 448 line = f.readline().rstrip() 449 450 def _get_function_locals(self, f): 451 f.readline() # "function locals:" 452 line = f.readline().rstrip() 453 while line: 454 function, name, value = self._get_fields(line, 3) 455 init_item(self.function_locals, function, dict) 456 if name != "{}": 457 self.function_locals[function][name] = decode_reference(value) 458 line = f.readline().rstrip() 459 460 def _get_function_targets(self, f): 461 f.readline() # "function targets:" 462 line = f.readline().rstrip() 463 while line: 464 function, n = self._get_fields(line) 465 self.importer.function_targets[function] = \ 466 self.function_targets[function] = int(n) 467 line = f.readline().rstrip() 468 469 def _get_function_arguments(self, f): 470 f.readline() # "function arguments:" 471 line = f.readline().rstrip() 472 while line: 473 function, n = self._get_fields(line) 474 self.importer.function_arguments[function] = \ 475 self.function_arguments[function] = int(n) 476 line = f.readline().rstrip() 477 478 def _get_attribute_usage(self, f): 479 f.readline() # "attribute usage:" 480 line = f.readline().rstrip() 481 while line: 482 unit, value = self._get_fields(line) 483 init_item(self.attr_usage, unit, dict) 484 self.usage_from_cache(value, self.attr_usage[unit]) 485 line = f.readline().rstrip() 486 487 def _get_attr_accesses(self, f): 488 f.readline() # "attribute accesses:" 489 line = f.readline().rstrip() 490 while line: 491 name, value = self._get_fields(line) 492 self.attr_accesses[name] = set(value.split(", ")) 493 line = f.readline().rstrip() 494 495 def _get_const_accesses(self, f): 496 f.readline() # "constant accesses:" 497 line = f.readline().rstrip() 498 while line: 499 name, original_name, attrnames, objpath, ref, remaining = self._get_fields(line, 6) 500 if attrnames == "{}": attrnames = None 501 init_item(self.const_accesses, name, dict) 502 self.const_accesses[name][(original_name, attrnames)] = (objpath, decode_reference(ref), remaining != "{}" and remaining or "") 503 line = f.readline().rstrip() 504 505 def _get_attr_accessors(self, f): 506 f.readline() # "attribute access usage:" 507 line = f.readline().rstrip() 508 while line: 509 objpath, name, attrname, value = self._get_fields(line, 4) 510 if attrname == "{}": attrname = None 511 access = name, attrname 512 init_item(self.attr_accessors, objpath, dict) 513 init_item(self.attr_accessors[objpath], access, list) 514 positions = map(int, value.split(", ")) 515 self.attr_accessors[objpath][access].append(positions) 516 line = f.readline().rstrip() 517 518 def _get_attr_access_modifiers(self, f): 519 f.readline() # "attribute access modifiers:" 520 line = f.readline().rstrip() 521 while line: 522 objpath, name, attrnames, value = self._get_fields(line, 4) 523 if name == "{}": name = None 524 if attrnames == "{}": attrnames = None 525 access = name, attrnames 526 init_item(self.attr_access_modifiers, objpath, dict) 527 init_item(self.attr_access_modifiers[objpath], access, list) 528 modifiers = [decode_modifier_term(s) for s in value] 529 self.attr_access_modifiers[objpath][access] = modifiers 530 line = f.readline().rstrip() 531 532 def _get_constant_literals(self, f): 533 f.readline() # "constant literals:" 534 line = f.readline().rstrip() 535 last_path = None 536 n = None 537 while line: 538 path, constant = self._get_fields(line) 539 if path != last_path: 540 n = 0 541 last_path = path 542 else: 543 n += 1 544 init_item(self.constants, path, dict) 545 self.constants[path][eval(constant)] = n 546 line = f.readline().rstrip() 547 548 def _get_constant_values(self, f): 549 f.readline() # "constant values:" 550 line = f.readline().rstrip() 551 while line: 552 name, value_type, value = self._get_fields(line, 3) 553 self.constant_values[name] = eval(value), value_type 554 line = f.readline().rstrip() 555 556 # Generic parsing methods. 557 558 def from_lines(self, f, d): 559 560 "Read lines from 'f', populating 'd'." 561 562 f.readline() # section heading 563 line = f.readline().rstrip() 564 while line: 565 name, value = self._get_fields(line) 566 d[name] = set(value and value.split(", ") or []) 567 line = f.readline().rstrip() 568 569 def usage_from_cache(self, value, mapping): 570 571 """ 572 Interpret the given 'value' containing name and usage information, 573 storing the information in the given 'mapping'. 574 """ 575 576 local, usage = self._get_fields(value) 577 init_item(mapping, local, list) 578 self._usage_from_cache(mapping[local], usage) 579 580 def _usage_from_cache(self, d, usage): 581 582 # Interpret descriptions of each version of the name. 583 584 all_usages = set() 585 for attrnames in usage.split("; "): 586 if attrnames == "{}": 587 all_attrnames = () 588 else: 589 # Decode attribute details for each usage description. 590 591 all_attrnames = set() 592 for attrname_str in attrnames.split(", "): 593 all_attrnames.add(attrname_str) 594 595 all_attrnames = list(all_attrnames) 596 all_attrnames.sort() 597 598 all_usages.add(tuple(all_attrnames)) 599 600 d.append(all_usages) 601 602 def _get_fields(self, s, n=2): 603 result = s.split(" ", n-1) 604 if len(result) == n: 605 return result 606 else: 607 return tuple(result) + tuple([""] * (n - len(result))) 608 609 class CacheWritingModule: 610 611 """ 612 A mix-in providing cache-writing support, to be combined with BasicModule. 613 """ 614 615 def to_cache(self, filename): 616 617 """ 618 Write a cached representation of the inspected module with the following 619 format to the file having the given 'filename': 620 621 filename 622 (empty line) 623 "imports:" 624 required module names 625 possibly required module names 626 "members:" 627 zero or more: qualified name " " reference 628 (empty line) 629 "class relationships:" 630 zero or more: qualified class name " " base class references 631 (empty line) 632 "instance attributes:" 633 zero or more: qualified class name " " instance attribute names 634 (empty line) 635 "instance attribute constants:" 636 zero or more: qualified class name " " attribute name " " reference 637 (empty line) 638 "names used:" 639 zero or more: qualified class/function/module name " " names 640 (empty line) 641 "names missing:" 642 zero or more: qualified class/function/module name " " names 643 (empty line) 644 "name references:" 645 zero or more: qualified name " " reference 646 (empty line) 647 "initialised names:" 648 zero or more: qualified name " " definition version " " reference 649 (empty line) 650 "aliased names:" 651 zero or more: qualified name " " definition version " " original name " " attribute names " " access number 652 (empty line) 653 "function parameters:" 654 zero or more: qualified function name " " parameter names 655 (empty line) 656 "function default parameters:" 657 zero or more: qualified function name " " parameter names with defaults 658 (empty line) 659 "function locals:" 660 zero or more: qualified function name " " local variable name " " reference 661 (empty line) 662 "scope globals:" 663 zero or more: qualified function name " " global variable names 664 (empty line) 665 "function targets:" 666 zero or more: qualified function name " " maximum number of targets allocated 667 (empty line) 668 "function arguments:" 669 zero or more: qualified function name " " maximum number of arguments allocated 670 (empty line) 671 "attribute usage:" 672 zero or more: qualified scope name " " local/global/qualified variable name " " usages 673 (empty line) 674 "attribute accesses:" 675 zero or more: qualified scope name " " attribute-chains 676 (empty line) 677 "constant accesses:" 678 zero or more: qualified function name " " attribute-chain " " reference " " remaining attribute-chain 679 (empty line) 680 "attribute access usage:" 681 zero or more: qualified function name " " local/global variable name " " attribute name " " definition versions 682 (empty line) 683 "attribute access modifiers:" 684 zero or more: qualified function name " " local/global variable name " " attribute name " " access modifiers 685 "constant literals:" 686 zero or more: qualified scope name " " constant literal 687 "constant values:" 688 zero or more: qualified name " " value type " " constant literal 689 690 All collections of names are separated by ", " characters. 691 692 References can be "<var>", a module name, or one of "<class>" or 693 "<function>" followed optionally by a ":" character and a qualified 694 name. 695 696 Parameter names with defaults are separated by ", " characters, with 697 each name followed by "=" and then followed by a reference. If "{}" is 698 indicated, no defaults are defined for the function. Similarly, function 699 locals may be indicated as "{}" meaning that there are no locals. 700 701 All usages (attribute usage sets) are separated by "; " characters, with 702 the special string "{}" representing an empty set. 703 704 Each usage is a collection of names separated by ", " characters, with 705 assigned attribute names suffixed with a "*" character. 706 707 Each attribute-chain expression is a dot-separated chain of attribute 708 names, with assignments suffixed with a "*" character. 709 710 Definition versions are separated by ", " characters and indicate the 711 name definition version associated with the access. 712 713 Access modifiers are separated by ", " characters and indicate features 714 of each access, with multiple accesses described on a single line. 715 """ 716 717 f = open(filename, "w") 718 try: 719 print >>f, self.filename 720 721 print >>f 722 print >>f, "imports:" 723 required = list(self.required) 724 required.sort() 725 print >>f, required and ", ".join(required) or "{}" 726 imports = list(self.imports) 727 imports.sort() 728 print >>f, imports and ", ".join(imports) or "{}" 729 730 print >>f 731 print >>f, "members:" 732 objects = self.objects.keys() 733 objects.sort() 734 for name in objects: 735 print >>f, name, self.objects[name] 736 737 print >>f 738 print >>f, "class relationships:" 739 classes = self.classes.keys() 740 classes.sort() 741 for class_ in classes: 742 bases = self.classes[class_] 743 if bases: 744 print >>f, class_, ", ".join(map(str, bases)) 745 else: 746 print >>f, class_ 747 748 self.to_lines(f, "instance attributes:", self.instance_attrs) 749 750 print >>f 751 print >>f, "instance attribute constants:" 752 classes = self.instance_attr_constants.items() 753 classes.sort() 754 for name, attrs in classes: 755 attrs = attrs.items() 756 attrs.sort() 757 for attrname, ref in attrs: 758 print >>f, name, attrname, ref 759 760 self.to_lines(f, "names used:", self.names_used) 761 self.to_lines(f, "names missing:", self.names_missing) 762 763 print >>f 764 print >>f, "name references:" 765 refs = self.name_references.items() 766 refs.sort() 767 for name, ref in refs: 768 print >>f, name, ref 769 770 print >>f 771 print >>f, "initialised names:" 772 assignments = self.initialised_names.items() 773 assignments.sort() 774 for name, refs in assignments: 775 versions = refs.items() 776 versions.sort() 777 for version, ref in versions: 778 print >>f, name, version, ref 779 780 print >>f 781 print >>f, "aliased names:" 782 assignments = self.aliased_names.items() 783 assignments.sort() 784 for name, aliases in assignments: 785 versions = aliases.items() 786 versions.sort() 787 for version, alias in versions: 788 original_name, attrnames, number = alias 789 print >>f, name, version, original_name, attrnames or "{}", number is None and "{}" or number 790 791 print >>f 792 print >>f, "function parameters:" 793 functions = self.function_parameters.keys() 794 functions.sort() 795 for function in functions: 796 print >>f, function, ", ".join(self.function_parameters[function]) 797 798 print >>f 799 print >>f, "function default parameters:" 800 functions = self.function_defaults.keys() 801 functions.sort() 802 for function in functions: 803 parameters = self.function_defaults[function] 804 if parameters: 805 print >>f, function, ", ".join([("%s=%s" % (name, default)) for (name, default) in parameters]) 806 else: 807 print >>f, function, "{}" 808 809 print >>f 810 print >>f, "function locals:" 811 functions = self.function_locals.keys() 812 functions.sort() 813 for function in functions: 814 names = self.function_locals[function].items() 815 if names: 816 names.sort() 817 for name, value in names: 818 print >>f, function, name, value 819 else: 820 print >>f, function, "{}" 821 822 self.to_lines(f, "scope globals:", self.scope_globals) 823 824 print >>f 825 print >>f, "function targets:" 826 functions = self.function_targets.keys() 827 functions.sort() 828 for function in functions: 829 print >>f, function, self.function_targets[function] 830 831 print >>f 832 print >>f, "function arguments:" 833 functions = self.function_arguments.keys() 834 functions.sort() 835 for function in functions: 836 print >>f, function, self.function_arguments[function] 837 838 print >>f 839 print >>f, "attribute usage:" 840 units = self.attr_usage.keys() 841 units.sort() 842 for unit in units: 843 d = self.attr_usage[unit] 844 self.usage_to_cache(d, f, unit) 845 846 print >>f 847 print >>f, "attribute accesses:" 848 paths = self.attr_accesses.keys() 849 paths.sort() 850 for path in paths: 851 accesses = list(self.attr_accesses[path]) 852 accesses.sort() 853 print >>f, path, ", ".join(accesses) 854 855 print >>f 856 print >>f, "constant accesses:" 857 paths = self.const_accesses.keys() 858 paths.sort() 859 for path in paths: 860 accesses = self.const_accesses[path].items() 861 accesses.sort() 862 for (original_name, attrnames), (objpath, ref, remaining_attrnames) in accesses: 863 print >>f, path, original_name, attrnames, objpath, ref, remaining_attrnames or "{}" 864 865 print >>f 866 print >>f, "attribute access usage:" 867 paths = self.attr_accessors.keys() 868 paths.sort() 869 for path in paths: 870 all_accesses = self.attr_accessors[path].items() 871 all_accesses.sort() 872 for (name, attrname), accesses in all_accesses: 873 for positions in accesses: 874 positions = map(str, positions) 875 print >>f, path, name, attrname or "{}", ", ".join(positions) 876 877 print >>f 878 print >>f, "attribute access modifiers:" 879 paths = self.attr_access_modifiers.keys() 880 paths.sort() 881 for path in paths: 882 all_accesses = self.attr_access_modifiers[path].items() 883 all_accesses.sort() 884 for (name, attrnames), modifiers in all_accesses: 885 print >>f, path, name or "{}", attrnames or "{}", encode_modifiers(modifiers) 886 887 print >>f 888 print >>f, "constant literals:" 889 paths = self.constants.keys() 890 paths.sort() 891 for path in paths: 892 constants = [(v, k) for (k, v) in self.constants[path].items()] 893 constants.sort() 894 for n, constant in constants: 895 print >>f, path, repr(constant) 896 897 print >>f 898 print >>f, "constant values:" 899 names = self.constant_values.keys() 900 names.sort() 901 for name in names: 902 value, value_type = self.constant_values[name] 903 print >>f, name, value_type, repr(value) 904 905 finally: 906 f.close() 907 908 def to_lines(self, f, heading, d): 909 910 "Write lines to 'f' with the given 'heading', using 'd'." 911 912 print >>f 913 print >>f, heading 914 keys = d.keys() 915 keys.sort() 916 for key in keys: 917 attrs = list(d[key]) 918 if attrs: 919 attrs.sort() 920 print >>f, key, ", ".join(attrs) 921 922 def usage_to_cache(self, details, f, prefix): 923 924 "Write the given namespace usage details to the cache." 925 926 names = list(details.keys()) 927 if names: 928 names.sort() 929 for name in names: 930 if details[name]: 931 932 # Produce descriptions for each version of the name. 933 934 for version in details[name]: 935 all_usages = [] 936 for usage in version: 937 all_usages.append(encode_usage(usage)) 938 939 print >>f, "%s %s %s" % (prefix, name, "; ".join(all_usages)) 940 941 # vim: tabstop=4 expandtab shiftwidth=4