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