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