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