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