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