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