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