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 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 if name != "{}": 507 self.function_locals[function][name] = decode_reference(value) 508 line = f.readline().rstrip() 509 510 def _get_function_targets(self, f): 511 f.readline() # "function targets:" 512 line = f.readline().rstrip() 513 while line: 514 function, n = self._get_fields(line) 515 self.importer.function_targets[function] = \ 516 self.function_targets[function] = int(n) 517 line = f.readline().rstrip() 518 519 def _get_function_arguments(self, f): 520 f.readline() # "function arguments:" 521 line = f.readline().rstrip() 522 while line: 523 function, n = self._get_fields(line) 524 self.importer.function_arguments[function] = \ 525 self.function_arguments[function] = int(n) 526 line = f.readline().rstrip() 527 528 def _get_attribute_usage(self, f): 529 f.readline() # "attribute usage:" 530 line = f.readline().rstrip() 531 while line: 532 unit, value = self._get_fields(line) 533 init_item(self.attr_usage, unit, dict) 534 self.usage_from_cache(value, self.attr_usage[unit]) 535 line = f.readline().rstrip() 536 537 def _get_attr_accesses(self, f): 538 f.readline() # "attribute accesses:" 539 line = f.readline().rstrip() 540 while line: 541 name, value = self._get_fields(line) 542 self.attr_accesses[name] = set(value.split(", ")) 543 line = f.readline().rstrip() 544 545 def _get_const_accesses(self, f): 546 f.readline() # "constant accesses:" 547 line = f.readline().rstrip() 548 while line: 549 name, original_name, attrnames, objpath, ref, remaining = self._get_fields(line, 6) 550 if attrnames == "{}": attrnames = None 551 init_item(self.const_accesses, name, dict) 552 self.const_accesses[name][(original_name, attrnames)] = (objpath, decode_reference(ref), remaining != "{}" and remaining or "") 553 line = f.readline().rstrip() 554 555 def _get_attr_accessors(self, f): 556 f.readline() # "attribute access usage:" 557 line = f.readline().rstrip() 558 while line: 559 objpath, name, attrname, value = self._get_fields(line, 4) 560 if attrname == "{}": attrname = None 561 access = name, attrname 562 init_item(self.attr_accessors, objpath, dict) 563 init_item(self.attr_accessors[objpath], access, list) 564 positions = map(int, value.split(", ")) 565 self.attr_accessors[objpath][access].append(positions) 566 line = f.readline().rstrip() 567 568 def _get_attr_access_modifiers(self, f): 569 f.readline() # "attribute access modifiers:" 570 line = f.readline().rstrip() 571 while line: 572 objpath, name, attrnames, value = self._get_fields(line, 4) 573 if name == "{}": name = None 574 if attrnames == "{}": attrnames = None 575 access = name, attrnames 576 init_item(self.attr_access_modifiers, objpath, dict) 577 init_item(self.attr_access_modifiers[objpath], access, list) 578 modifiers = [decode_modifier_term(s) for s in value] 579 self.attr_access_modifiers[objpath][access] = modifiers 580 line = f.readline().rstrip() 581 582 def _get_constant_literals(self, f): 583 f.readline() # "constant literals:" 584 line = f.readline().rstrip() 585 last_path = None 586 n = None 587 while line: 588 path, constant = self._get_fields(line) 589 if path != last_path: 590 n = 0 591 last_path = path 592 else: 593 n += 1 594 init_item(self.constants, path, dict) 595 self.constants[path][eval(constant)] = n 596 line = f.readline().rstrip() 597 598 def _get_constant_values(self, f): 599 f.readline() # "constant values:" 600 line = f.readline().rstrip() 601 while line: 602 name, value_type, value = self._get_fields(line, 3) 603 self.constant_values[name] = eval(value), value_type 604 line = f.readline().rstrip() 605 606 # Generic parsing methods. 607 608 def from_lines(self, f, d): 609 610 "Read lines from 'f', populating 'd'." 611 612 f.readline() # section heading 613 line = f.readline().rstrip() 614 while line: 615 name, value = self._get_fields(line) 616 d[name] = set(value and value.split(", ") or []) 617 line = f.readline().rstrip() 618 619 def usage_from_cache(self, value, mapping): 620 621 """ 622 Interpret the given 'value' containing name and usage information, 623 storing the information in the given 'mapping'. 624 """ 625 626 local, usage = self._get_fields(value) 627 init_item(mapping, local, list) 628 self._usage_from_cache(mapping[local], usage) 629 630 def _usage_from_cache(self, d, usage): 631 632 # Interpret descriptions of each version of the name. 633 634 all_usages = set() 635 for attrnames in usage.split("; "): 636 if attrnames == "{}": 637 all_attrnames = () 638 else: 639 # Decode attribute details for each usage description. 640 641 all_attrnames = set() 642 for attrname_str in attrnames.split(", "): 643 all_attrnames.add(attrname_str) 644 645 all_attrnames = list(all_attrnames) 646 all_attrnames.sort() 647 648 all_usages.add(tuple(all_attrnames)) 649 650 d.append(all_usages) 651 652 def _get_fields(self, s, n=2): 653 result = s.split(" ", n-1) 654 if len(result) == n: 655 return result 656 else: 657 return tuple(result) + tuple([""] * (n - len(result))) 658 659 class CacheWritingModule: 660 661 """ 662 A mix-in providing cache-writing support, to be combined with BasicModule. 663 """ 664 665 def to_cache(self, filename): 666 667 """ 668 Write a cached representation of the inspected module with the following 669 format to the file having the given 'filename': 670 671 filename 672 (empty line) 673 "imports:" 674 required module names 675 possibly required module names 676 "members:" 677 zero or more: qualified name " " reference 678 (empty line) 679 "class relationships:" 680 zero or more: qualified class name " " base class references 681 (empty line) 682 "instance attributes:" 683 zero or more: qualified class name " " instance attribute names 684 (empty line) 685 "instance attribute constants:" 686 zero or more: qualified class name " " attribute name " " reference 687 (empty line) 688 "names used:" 689 zero or more: qualified class/function/module name " " names 690 (empty line) 691 "names missing:" 692 zero or more: qualified class/function/module name " " names 693 (empty line) 694 "name references:" 695 zero or more: qualified name " " reference 696 (empty line) 697 "initialised names:" 698 zero or more: qualified name " " definition version " " reference 699 (empty line) 700 "aliased names:" 701 zero or more: qualified name " " definition version " " original name " " attribute names " " access number 702 (empty line) 703 "function parameters:" 704 zero or more: qualified function name " " parameter names 705 (empty line) 706 "function default parameters:" 707 zero or more: qualified function name " " parameter names with defaults 708 (empty line) 709 "function locals:" 710 zero or more: qualified function name " " local variable name " " reference 711 (empty line) 712 "scope globals:" 713 zero or more: qualified function name " " global variable names 714 (empty line) 715 "function targets:" 716 zero or more: qualified function name " " maximum number of targets allocated 717 (empty line) 718 "function arguments:" 719 zero or more: qualified function name " " maximum number of arguments allocated 720 (empty line) 721 "attribute usage:" 722 zero or more: qualified scope name " " local/global/qualified variable name " " usages 723 (empty line) 724 "attribute accesses:" 725 zero or more: qualified scope name " " attribute-chains 726 (empty line) 727 "constant accesses:" 728 zero or more: qualified function name " " attribute-chain " " reference " " remaining attribute-chain 729 (empty line) 730 "attribute access usage:" 731 zero or more: qualified function name " " local/global variable name " " attribute name " " definition versions 732 (empty line) 733 "attribute access modifiers:" 734 zero or more: qualified function name " " local/global variable name " " attribute name " " access modifiers 735 "constant literals:" 736 zero or more: qualified scope name " " constant literal 737 "constant values:" 738 zero or more: qualified name " " value type " " constant literal 739 740 All collections of names are separated by ", " characters. 741 742 References can be "<var>", a module name, or one of "<class>" or 743 "<function>" followed optionally by a ":" character and a qualified 744 name. 745 746 Parameter names with defaults are separated by ", " characters, with 747 each name followed by "=" and then followed by a reference. If "{}" is 748 indicated, no defaults are defined for the function. Similarly, function 749 locals may be indicated as "{}" meaning that there are no locals. 750 751 All usages (attribute usage sets) are separated by "; " characters, with 752 the special string "{}" representing an empty set. 753 754 Each usage is a collection of names separated by ", " characters, with 755 assigned attribute names suffixed with a "*" character. 756 757 Each attribute-chain expression is a dot-separated chain of attribute 758 names, with assignments suffixed with a "*" character. 759 760 Definition versions are separated by ", " characters and indicate the 761 name definition version associated with the access. 762 763 Access modifiers are separated by ", " characters and indicate features 764 of each access, with multiple accesses described on a single line. 765 """ 766 767 f = open(filename, "w") 768 try: 769 print >>f, self.filename 770 771 print >>f 772 print >>f, "imports:" 773 required = list(self.required) 774 required.sort() 775 print >>f, required and ", ".join(required) or "{}" 776 imports = list(self.imports) 777 imports.sort() 778 print >>f, imports and ", ".join(imports) or "{}" 779 780 print >>f 781 print >>f, "members:" 782 objects = self.objects.keys() 783 objects.sort() 784 for name in objects: 785 print >>f, name, self.objects[name] 786 787 print >>f 788 print >>f, "class relationships:" 789 classes = self.classes.keys() 790 classes.sort() 791 for class_ in classes: 792 bases = self.classes[class_] 793 if bases: 794 print >>f, class_, ", ".join(map(str, bases)) 795 else: 796 print >>f, class_ 797 798 self.to_lines(f, "instance attributes:", self.instance_attrs) 799 800 print >>f 801 print >>f, "instance attribute constants:" 802 classes = self.instance_attr_constants.items() 803 classes.sort() 804 for name, attrs in classes: 805 attrs = attrs.items() 806 attrs.sort() 807 for attrname, ref in attrs: 808 print >>f, name, attrname, ref 809 810 self.to_lines(f, "names used:", self.names_used) 811 812 print >>f 813 print >>f, "name references:" 814 refs = self.name_references.items() 815 refs.sort() 816 for name, ref in refs: 817 print >>f, name, ref 818 819 print >>f 820 print >>f, "initialised names:" 821 assignments = self.initialised_names.items() 822 assignments.sort() 823 for name, refs in assignments: 824 versions = refs.items() 825 versions.sort() 826 for version, ref in versions: 827 print >>f, name, version, ref 828 829 print >>f 830 print >>f, "aliased names:" 831 assignments = self.aliased_names.items() 832 assignments.sort() 833 for name, aliases in assignments: 834 versions = aliases.items() 835 versions.sort() 836 for version, alias in versions: 837 original_name, attrnames, number = alias 838 print >>f, name, version, original_name, attrnames or "{}", number is None and "{}" or number 839 840 print >>f 841 print >>f, "function parameters:" 842 functions = self.function_parameters.keys() 843 functions.sort() 844 for function in functions: 845 parameters = self.function_parameters[function] 846 if parameters: 847 print >>f, function, ", ".join(parameters) 848 else: 849 print >>f, function, "{}" 850 851 print >>f 852 print >>f, "function default parameters:" 853 functions = self.function_defaults.keys() 854 functions.sort() 855 for function in functions: 856 parameters = self.function_defaults[function] 857 if parameters: 858 print >>f, function, ", ".join([("%s=%s" % (name, default)) for (name, default) in parameters]) 859 else: 860 print >>f, function, "{}" 861 862 print >>f 863 print >>f, "function locals:" 864 functions = self.function_locals.keys() 865 functions.sort() 866 for function in functions: 867 names = self.function_locals[function].items() 868 if names: 869 names.sort() 870 for name, value in names: 871 print >>f, function, name, value 872 else: 873 print >>f, function, "{}" 874 875 self.to_lines(f, "scope globals:", self.scope_globals) 876 877 print >>f 878 print >>f, "function targets:" 879 functions = self.function_targets.keys() 880 functions.sort() 881 for function in functions: 882 print >>f, function, self.function_targets[function] 883 884 print >>f 885 print >>f, "function arguments:" 886 functions = self.function_arguments.keys() 887 functions.sort() 888 for function in functions: 889 print >>f, function, self.function_arguments[function] 890 891 print >>f 892 print >>f, "attribute usage:" 893 units = self.attr_usage.keys() 894 units.sort() 895 for unit in units: 896 d = self.attr_usage[unit] 897 self.usage_to_cache(d, f, unit) 898 899 print >>f 900 print >>f, "attribute accesses:" 901 paths = self.attr_accesses.keys() 902 paths.sort() 903 for path in paths: 904 accesses = list(self.attr_accesses[path]) 905 accesses.sort() 906 print >>f, path, ", ".join(accesses) 907 908 print >>f 909 print >>f, "constant accesses:" 910 paths = self.const_accesses.keys() 911 paths.sort() 912 for path in paths: 913 accesses = self.const_accesses[path].items() 914 accesses.sort() 915 for (original_name, attrnames), (objpath, ref, remaining_attrnames) in accesses: 916 print >>f, path, original_name, attrnames, objpath, ref, remaining_attrnames or "{}" 917 918 print >>f 919 print >>f, "attribute access usage:" 920 paths = self.attr_accessors.keys() 921 paths.sort() 922 for path in paths: 923 all_accesses = self.attr_accessors[path].items() 924 all_accesses.sort() 925 for (name, attrname), accesses in all_accesses: 926 for positions in accesses: 927 positions = map(str, positions) 928 print >>f, path, name, attrname or "{}", ", ".join(positions) 929 930 print >>f 931 print >>f, "attribute access modifiers:" 932 paths = self.attr_access_modifiers.keys() 933 paths.sort() 934 for path in paths: 935 all_accesses = self.attr_access_modifiers[path].items() 936 all_accesses.sort() 937 for (name, attrnames), modifiers in all_accesses: 938 print >>f, path, name or "{}", attrnames or "{}", encode_modifiers(modifiers) 939 940 print >>f 941 print >>f, "constant literals:" 942 paths = self.constants.keys() 943 paths.sort() 944 for path in paths: 945 constants = [(v, k) for (k, v) in self.constants[path].items()] 946 constants.sort() 947 for n, constant in constants: 948 print >>f, path, repr(constant) 949 950 print >>f 951 print >>f, "constant values:" 952 names = self.constant_values.keys() 953 names.sort() 954 for name in names: 955 value, value_type = self.constant_values[name] 956 print >>f, name, value_type, repr(value) 957 958 finally: 959 f.close() 960 961 def to_lines(self, f, heading, d): 962 963 "Write lines to 'f' with the given 'heading', using 'd'." 964 965 print >>f 966 print >>f, heading 967 keys = d.keys() 968 keys.sort() 969 for key in keys: 970 attrs = list(d[key]) 971 if attrs: 972 attrs.sort() 973 print >>f, key, ", ".join(attrs) 974 975 def usage_to_cache(self, details, f, prefix): 976 977 "Write the given namespace usage details to the cache." 978 979 names = list(details.keys()) 980 if names: 981 names.sort() 982 for name in names: 983 if details[name]: 984 985 # Produce descriptions for each version of the name. 986 987 for version in details[name]: 988 all_usages = [] 989 for usage in version: 990 all_usages.append(encode_usage(usage)) 991 992 print >>f, "%s %s %s" % (prefix, name, "; ".join(all_usages)) 993 994 # vim: tabstop=4 expandtab shiftwidth=4