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