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 class CachedModule(BasicModule): 278 279 "A cached module." 280 281 def __repr__(self): 282 return "CachedModule(%r, %r)" % (self.name, self.importer) 283 284 def to_cache(self, filename): 285 286 "Not actually writing the module back to 'filename'." 287 288 pass 289 290 def from_cache(self, filename): 291 292 """ 293 Read a module's details from the file with the given 'filename' as 294 described in the to_cache method of InspectedModule. 295 """ 296 297 f = open(filename) 298 try: 299 self.filename = f.readline().rstrip() 300 301 f.readline() # (empty line) 302 303 self._get_members(f) 304 self._get_class_relationships(f) 305 self._get_instance_attrs(f) 306 self._get_instance_attr_constants(f) 307 self.from_lines(f, self.names_used) # "names used:" 308 self.from_lines(f, self.names_missing) # "names missing:" 309 self._get_name_references(f) 310 self._get_initialised_names(f) 311 self._get_aliased_names(f) 312 self._get_function_parameters(f) 313 self._get_function_defaults(f) 314 self._get_function_locals(f) 315 self.from_lines(f, self.scope_globals) # "scope globals:" 316 self._get_function_targets(f) 317 self._get_function_arguments(f) 318 self._get_attribute_usage(f) 319 self._get_attr_accesses(f) 320 self._get_const_accesses(f) 321 self._get_attr_accessors(f) 322 self._get_attr_access_modifiers(f) 323 self._get_constant_literals(f) 324 self._get_constant_values(f) 325 326 finally: 327 f.close() 328 329 def complete(self): 330 self.propagate() 331 332 def _get_members(self, f): 333 f.readline() # "members:" 334 line = f.readline().rstrip() 335 while line: 336 name, ref = line.split(" ", 1) 337 self.set_object(name, ref) 338 line = f.readline().rstrip() 339 340 def _get_class_relationships(self, f): 341 f.readline() # "class relationships:" 342 line = f.readline().rstrip() 343 while line: 344 name, value = self._get_fields(line) 345 values = value and value.split(", ") or [] 346 self.importer.classes[name] = self.classes[name] = map(decode_reference, values) 347 self.importer.subclasses[name] = set() 348 line = f.readline().rstrip() 349 350 def _get_instance_attrs(self, f): 351 f.readline() # "instance attributes:" 352 line = f.readline().rstrip() 353 while line: 354 name, value = self._get_fields(line) 355 self.importer.all_instance_attrs[name] = self.instance_attrs[name] = set(value and value.split(", ") or []) 356 line = f.readline().rstrip() 357 358 def _get_instance_attr_constants(self, f): 359 f.readline() # "instance attribute constants:" 360 line = f.readline().rstrip() 361 while line: 362 name, attrname, ref = self._get_fields(line, 3) 363 init_item(self.instance_attr_constants, name, dict) 364 self.instance_attr_constants[name][attrname] = decode_reference(ref) 365 line = f.readline().rstrip() 366 367 def _get_name_references(self, f): 368 f.readline() # "name references:" 369 line = f.readline().rstrip() 370 while line: 371 name, value = self._get_fields(line) 372 self.name_references[name] = value 373 line = f.readline().rstrip() 374 375 def _get_initialised_names(self, f): 376 f.readline() # "initialised names:" 377 line = f.readline().rstrip() 378 while line: 379 name, version, value = self._get_fields(line, 3) 380 init_item(self.initialised_names, name, dict) 381 self.initialised_names[name][int(version)] = decode_reference(value) 382 line = f.readline().rstrip() 383 384 def _get_aliased_names(self, f): 385 f.readline() # "aliased names:" 386 line = f.readline().rstrip() 387 while line: 388 name, version, original_name, attrnames, number = self._get_fields(line, 5) 389 init_item(self.aliased_names, name, dict) 390 if number == "{}": number = None 391 else: number = int(number) 392 self.aliased_names[name][int(version)] = (original_name, attrnames != "{}" and attrnames or None, number) 393 line = f.readline().rstrip() 394 395 def _get_function_parameters(self, f): 396 f.readline() # "function parameters:" 397 line = f.readline().rstrip() 398 while line: 399 function, names = self._get_fields(line) 400 self.importer.function_parameters[function] = \ 401 self.function_parameters[function] = names and names.split(", ") or [] 402 line = f.readline().rstrip() 403 404 def _get_function_defaults(self, f): 405 f.readline() # "function default parameters:" 406 line = f.readline().rstrip() 407 while line: 408 function, defaults = self._get_fields(line) 409 self.importer.function_defaults[function] = \ 410 self.function_defaults[function] = l = [] 411 if defaults != "{}": 412 for value in defaults.split(", "): 413 name, default = value.split("=") 414 default = decode_reference(default) 415 l.append((name, default)) 416 line = f.readline().rstrip() 417 418 def _get_function_locals(self, f): 419 f.readline() # "function locals:" 420 line = f.readline().rstrip() 421 while line: 422 function, name, value = self._get_fields(line, 3) 423 init_item(self.function_locals, function, dict) 424 if name != "{}": 425 self.function_locals[function][name] = decode_reference(value) 426 line = f.readline().rstrip() 427 428 def _get_function_targets(self, f): 429 f.readline() # "function targets:" 430 line = f.readline().rstrip() 431 while line: 432 function, n = self._get_fields(line) 433 self.importer.function_targets[function] = \ 434 self.function_targets[function] = int(n) 435 line = f.readline().rstrip() 436 437 def _get_function_arguments(self, f): 438 f.readline() # "function arguments:" 439 line = f.readline().rstrip() 440 while line: 441 function, n = self._get_fields(line) 442 self.importer.function_arguments[function] = \ 443 self.function_arguments[function] = int(n) 444 line = f.readline().rstrip() 445 446 def _get_attribute_usage(self, f): 447 f.readline() # "attribute usage:" 448 line = f.readline().rstrip() 449 while line: 450 unit, value = self._get_fields(line) 451 init_item(self.attr_usage, unit, dict) 452 self.usage_from_cache(value, self.attr_usage[unit]) 453 line = f.readline().rstrip() 454 455 def _get_attr_accesses(self, f): 456 f.readline() # "attribute accesses:" 457 line = f.readline().rstrip() 458 while line: 459 name, value = self._get_fields(line) 460 self.attr_accesses[name] = set(value.split(", ")) 461 line = f.readline().rstrip() 462 463 def _get_const_accesses(self, f): 464 f.readline() # "constant accesses:" 465 line = f.readline().rstrip() 466 while line: 467 name, original_name, attrnames, objpath, ref, remaining = self._get_fields(line, 6) 468 if attrnames == "{}": attrnames = None 469 init_item(self.const_accesses, name, dict) 470 self.const_accesses[name][(original_name, attrnames)] = (objpath, decode_reference(ref), remaining != "{}" and remaining or "") 471 line = f.readline().rstrip() 472 473 def _get_attr_accessors(self, f): 474 f.readline() # "attribute access usage:" 475 line = f.readline().rstrip() 476 while line: 477 objpath, name, attrname, value = self._get_fields(line, 4) 478 if attrname == "{}": attrname = None 479 access = name, attrname 480 init_item(self.attr_accessors, objpath, dict) 481 init_item(self.attr_accessors[objpath], access, list) 482 positions = map(int, value.split(", ")) 483 self.attr_accessors[objpath][access].append(positions) 484 line = f.readline().rstrip() 485 486 def _get_attr_access_modifiers(self, f): 487 f.readline() # "attribute access modifiers:" 488 line = f.readline().rstrip() 489 while line: 490 objpath, name, attrnames, value = self._get_fields(line, 4) 491 if name == "{}": name = None 492 if attrnames == "{}": attrnames = None 493 access = name, attrnames 494 init_item(self.attr_access_modifiers, objpath, dict) 495 init_item(self.attr_access_modifiers[objpath], access, list) 496 modifiers = [decode_modifier_term(s) for s in value] 497 self.attr_access_modifiers[objpath][access] = modifiers 498 line = f.readline().rstrip() 499 500 def _get_constant_literals(self, f): 501 f.readline() # "constant literals:" 502 line = f.readline().rstrip() 503 last_path = None 504 n = None 505 while line: 506 path, constant = self._get_fields(line) 507 if path != last_path: 508 n = 0 509 last_path = path 510 else: 511 n += 1 512 init_item(self.constants, path, dict) 513 self.constants[path][eval(constant)] = n 514 line = f.readline().rstrip() 515 516 def _get_constant_values(self, f): 517 f.readline() # "constant values:" 518 line = f.readline().rstrip() 519 while line: 520 name, value_type, value = self._get_fields(line, 3) 521 self.constant_values[name] = eval(value), value_type 522 line = f.readline().rstrip() 523 524 # Generic parsing methods. 525 526 def from_lines(self, f, d): 527 528 "Read lines from 'f', populating 'd'." 529 530 f.readline() # section heading 531 line = f.readline().rstrip() 532 while line: 533 name, value = self._get_fields(line) 534 d[name] = set(value and value.split(", ") or []) 535 line = f.readline().rstrip() 536 537 def usage_from_cache(self, value, mapping): 538 539 """ 540 Interpret the given 'value' containing name and usage information, 541 storing the information in the given 'mapping'. 542 """ 543 544 local, usage = self._get_fields(value) 545 init_item(mapping, local, list) 546 self._usage_from_cache(mapping[local], usage) 547 548 def _usage_from_cache(self, d, usage): 549 550 # Interpret descriptions of each version of the name. 551 552 all_usages = set() 553 for attrnames in usage.split("; "): 554 if attrnames == "{}": 555 all_attrnames = () 556 else: 557 # Decode attribute details for each usage description. 558 559 all_attrnames = set() 560 for attrname_str in attrnames.split(", "): 561 all_attrnames.add(attrname_str) 562 563 all_attrnames = list(all_attrnames) 564 all_attrnames.sort() 565 566 all_usages.add(tuple(all_attrnames)) 567 568 d.append(all_usages) 569 570 def _get_fields(self, s, n=2): 571 result = s.split(" ", n-1) 572 if len(result) == n: 573 return result 574 else: 575 return tuple(result) + tuple([""] * (n - len(result))) 576 577 class CacheWritingModule: 578 579 """ 580 A mix-in providing cache-writing support, to be combined with BasicModule. 581 """ 582 583 def to_cache(self, filename): 584 585 """ 586 Write a cached representation of the inspected module with the following 587 format to the file having the given 'filename': 588 589 filename 590 (empty line) 591 "members:" 592 zero or more: qualified name " " reference 593 (empty line) 594 "class relationships:" 595 zero or more: qualified class name " " base class references 596 (empty line) 597 "instance attributes:" 598 zero or more: qualified class name " " instance attribute names 599 (empty line) 600 "instance attribute constants:" 601 zero or more: qualified class name " " attribute name " " reference 602 (empty line) 603 "names used:" 604 zero or more: qualified class/function/module name " " names 605 (empty line) 606 "names missing:" 607 zero or more: qualified class/function/module name " " names 608 (empty line) 609 "name references:" 610 zero or more: qualified name " " reference 611 (empty line) 612 "initialised names:" 613 zero or more: qualified name " " definition version " " reference 614 (empty line) 615 "aliased names:" 616 zero or more: qualified name " " definition version " " original name " " attribute names " " access number 617 (empty line) 618 "function parameters:" 619 zero or more: qualified function name " " parameter names 620 (empty line) 621 "function default parameters:" 622 zero or more: qualified function name " " parameter names with defaults 623 (empty line) 624 "function locals:" 625 zero or more: qualified function name " " local variable name " " reference 626 (empty line) 627 "scope globals:" 628 zero or more: qualified function name " " global variable names 629 (empty line) 630 "function targets:" 631 zero or more: qualified function name " " maximum number of targets allocated 632 (empty line) 633 "function arguments:" 634 zero or more: qualified function name " " maximum number of arguments allocated 635 (empty line) 636 "attribute usage:" 637 zero or more: qualified scope name " " local/global/qualified variable name " " usages 638 (empty line) 639 "attribute accesses:" 640 zero or more: qualified scope name " " attribute-chains 641 (empty line) 642 "constant accesses:" 643 zero or more: qualified function name " " attribute-chain " " reference " " remaining attribute-chain 644 (empty line) 645 "attribute access usage:" 646 zero or more: qualified function name " " local/global variable name " " attribute name " " definition versions 647 (empty line) 648 "attribute access modifiers:" 649 zero or more: qualified function name " " local/global variable name " " attribute name " " access modifiers 650 "constant literals:" 651 zero or more: qualified scope name " " constant literal 652 "constant values:" 653 zero or more: qualified name " " value type " " constant literal 654 655 All collections of names are separated by ", " characters. 656 657 References can be "<var>", a module name, or one of "<class>" or 658 "<function>" followed optionally by a ":" character and a qualified 659 name. 660 661 Parameter names with defaults are separated by ", " characters, with 662 each name followed by "=" and then followed by a reference. If "{}" is 663 indicated, no defaults are defined for the function. Similarly, function 664 locals may be indicated as "{}" meaning that there are no locals. 665 666 All usages (attribute usage sets) are separated by "; " characters, with 667 the special string "{}" representing an empty set. 668 669 Each usage is a collection of names separated by ", " characters, with 670 assigned attribute names suffixed with a "*" character. 671 672 Each attribute-chain expression is a dot-separated chain of attribute 673 names, with assignments suffixed with a "*" character. 674 675 Definition versions are separated by ", " characters and indicate the 676 name definition version associated with the access. 677 678 Access modifiers are separated by ", " characters and indicate features 679 of each access, with multiple accesses described on a single line. 680 """ 681 682 f = open(filename, "w") 683 try: 684 print >>f, self.filename 685 686 print >>f 687 print >>f, "members:" 688 objects = self.objects.keys() 689 objects.sort() 690 for name in objects: 691 print >>f, name, self.objects[name] 692 693 print >>f 694 print >>f, "class relationships:" 695 classes = self.classes.keys() 696 classes.sort() 697 for class_ in classes: 698 bases = self.classes[class_] 699 if bases: 700 print >>f, class_, ", ".join(map(str, bases)) 701 else: 702 print >>f, class_ 703 704 self.to_lines(f, "instance attributes:", self.instance_attrs) 705 706 print >>f 707 print >>f, "instance attribute constants:" 708 classes = self.instance_attr_constants.items() 709 classes.sort() 710 for name, attrs in classes: 711 attrs = attrs.items() 712 attrs.sort() 713 for attrname, ref in attrs: 714 print >>f, name, attrname, ref 715 716 self.to_lines(f, "names used:", self.names_used) 717 self.to_lines(f, "names missing:", self.names_missing) 718 719 print >>f 720 print >>f, "name references:" 721 refs = self.name_references.items() 722 refs.sort() 723 for name, ref in refs: 724 print >>f, name, ref 725 726 print >>f 727 print >>f, "initialised names:" 728 assignments = self.initialised_names.items() 729 assignments.sort() 730 for name, refs in assignments: 731 versions = refs.items() 732 versions.sort() 733 for version, ref in versions: 734 print >>f, name, version, ref 735 736 print >>f 737 print >>f, "aliased names:" 738 assignments = self.aliased_names.items() 739 assignments.sort() 740 for name, aliases in assignments: 741 versions = aliases.items() 742 versions.sort() 743 for version, alias in versions: 744 original_name, attrnames, number = alias 745 print >>f, name, version, original_name, attrnames or "{}", number is None and "{}" or number 746 747 print >>f 748 print >>f, "function parameters:" 749 functions = self.function_parameters.keys() 750 functions.sort() 751 for function in functions: 752 print >>f, function, ", ".join(self.function_parameters[function]) 753 754 print >>f 755 print >>f, "function default parameters:" 756 functions = self.function_defaults.keys() 757 functions.sort() 758 for function in functions: 759 parameters = self.function_defaults[function] 760 if parameters: 761 print >>f, function, ", ".join([("%s=%s" % (name, default)) for (name, default) in parameters]) 762 else: 763 print >>f, function, "{}" 764 765 print >>f 766 print >>f, "function locals:" 767 functions = self.function_locals.keys() 768 functions.sort() 769 for function in functions: 770 names = self.function_locals[function].items() 771 if names: 772 names.sort() 773 for name, value in names: 774 print >>f, function, name, value 775 else: 776 print >>f, function, "{}" 777 778 self.to_lines(f, "scope globals:", self.scope_globals) 779 780 print >>f 781 print >>f, "function targets:" 782 functions = self.function_targets.keys() 783 functions.sort() 784 for function in functions: 785 print >>f, function, self.function_targets[function] 786 787 print >>f 788 print >>f, "function arguments:" 789 functions = self.function_arguments.keys() 790 functions.sort() 791 for function in functions: 792 print >>f, function, self.function_arguments[function] 793 794 print >>f 795 print >>f, "attribute usage:" 796 units = self.attr_usage.keys() 797 units.sort() 798 for unit in units: 799 d = self.attr_usage[unit] 800 self.usage_to_cache(d, f, unit) 801 802 print >>f 803 print >>f, "attribute accesses:" 804 paths = self.attr_accesses.keys() 805 paths.sort() 806 for path in paths: 807 accesses = list(self.attr_accesses[path]) 808 accesses.sort() 809 print >>f, path, ", ".join(accesses) 810 811 print >>f 812 print >>f, "constant accesses:" 813 paths = self.const_accesses.keys() 814 paths.sort() 815 for path in paths: 816 accesses = self.const_accesses[path].items() 817 accesses.sort() 818 for (original_name, attrnames), (objpath, ref, remaining_attrnames) in accesses: 819 print >>f, path, original_name, attrnames, objpath, ref, remaining_attrnames or "{}" 820 821 print >>f 822 print >>f, "attribute access usage:" 823 paths = self.attr_accessors.keys() 824 paths.sort() 825 for path in paths: 826 all_accesses = self.attr_accessors[path].items() 827 all_accesses.sort() 828 for (name, attrname), accesses in all_accesses: 829 for positions in accesses: 830 positions = map(str, positions) 831 print >>f, path, name, attrname or "{}", ", ".join(positions) 832 833 print >>f 834 print >>f, "attribute access modifiers:" 835 paths = self.attr_access_modifiers.keys() 836 paths.sort() 837 for path in paths: 838 all_accesses = self.attr_access_modifiers[path].items() 839 all_accesses.sort() 840 for (name, attrnames), modifiers in all_accesses: 841 print >>f, path, name or "{}", attrnames or "{}", encode_modifiers(modifiers) 842 843 print >>f 844 print >>f, "constant literals:" 845 paths = self.constants.keys() 846 paths.sort() 847 for path in paths: 848 constants = [(v, k) for (k, v) in self.constants[path].items()] 849 constants.sort() 850 for n, constant in constants: 851 print >>f, path, repr(constant) 852 853 print >>f 854 print >>f, "constant values:" 855 names = self.constant_values.keys() 856 names.sort() 857 for name in names: 858 value, value_type = self.constant_values[name] 859 print >>f, name, value_type, repr(value) 860 861 finally: 862 f.close() 863 864 def to_lines(self, f, heading, d): 865 866 "Write lines to 'f' with the given 'heading', using 'd'." 867 868 print >>f 869 print >>f, heading 870 keys = d.keys() 871 keys.sort() 872 for key in keys: 873 attrs = list(d[key]) 874 if attrs: 875 attrs.sort() 876 print >>f, key, ", ".join(attrs) 877 878 def usage_to_cache(self, details, f, prefix): 879 880 "Write the given namespace usage details to the cache." 881 882 names = list(details.keys()) 883 if names: 884 names.sort() 885 for name in names: 886 if details[name]: 887 888 # Produce descriptions for each version of the name. 889 890 for version in details[name]: 891 all_usages = [] 892 for usage in version: 893 all_usages.append(encode_usage(usage)) 894 895 print >>f, "%s %s %s" % (prefix, name, "; ".join(all_usages)) 896 897 # vim: tabstop=4 expandtab shiftwidth=4