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