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