1 #!/usr/bin/env python 2 3 """ 4 Data classes. 5 6 Copyright (C) 2007, 2008 Paul Boddie <paul@boddie.org.uk> 7 8 This program is free software; you can redistribute it and/or modify it under 9 the terms of the GNU General Public License as published by the Free Software 10 Foundation; either version 3 of the License, or (at your option) any later 11 version. 12 13 This program is distributed in the hope that it will be useful, but WITHOUT 14 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 15 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 16 details. 17 18 You should have received a copy of the GNU General Public License along with 19 this program. If not, see <http://www.gnu.org/licenses/>. 20 21 -------- 22 23 The central classes in this module are the following: 24 25 * Class 26 * Function 27 * Module 28 29 All of the above support the Naming interface either explicitly or through 30 general conformance, meaning that all can be asked to provide their 'full_name' 31 using the method of that name. 32 33 Additionally, all of the above also support a dictionary interface in order to 34 access names within their defined scopes. Specific methods also exist in order 35 to distinguish between certain kinds of attributes: 36 37 * Class: (class|all_class|instance|all)_attributes 38 * Function: parameters, locals, all_locals 39 * Module: module_attributes 40 41 These specific methods are useful in certain situations. 42 43 The above classes also provide an 'astnode' attribute, indicating the AST node 44 where each such object is defined. 45 """ 46 47 from micropython.common import * 48 49 def shortrepr(obj): 50 if obj is None: 51 return repr(None) 52 else: 53 return obj.__shortrepr__() 54 55 # Mix-ins and abstract classes. 56 57 class NamespaceDict: 58 59 "A mix-in providing dictionary methods." 60 61 def __init__(self, global_namespace=None): 62 self.namespace = {} 63 self.globals = set() 64 self.global_namespace = global_namespace 65 self.finalised = 0 66 67 def __delitem__(self, name): 68 del self.namespace[name] 69 70 def has_key(self, name): 71 return self.namespace.has_key(name) 72 73 def keys(self): 74 return self.namespace.keys() 75 76 def values(self): 77 return self.namespace.values() 78 79 def items(self): 80 return self.namespace.items() 81 82 def __getitem__(self, name): 83 return self.namespace[name] 84 85 def get(self, name, default=None): 86 return self.namespace.get(name, default) 87 88 def __setitem__(self, name, value): 89 self.set(name, value) 90 91 def set(self, name, value, single_assignment=1): 92 93 """ 94 A more powerful set operation, making 'name' refer to 'value' whilst 95 indicating whether a 'single_assignment' (true by default) occurs in 96 this operation (or whether the operation covers potentially many 97 assignments in the lifetime of a program). 98 """ 99 100 if name in self.globals: 101 self.global_namespace.set(name, value, 0) 102 else: 103 attr = self._set(name, value) 104 attr.update(attr.value, single_assignment) 105 106 def set_module(self, name, value): 107 108 """ 109 A specialised set operation, making 'name' refer to 'value' in the 110 context of making a module reference available in association with 111 'name' as part of the import of that module or a submodule of that 112 module. 113 """ 114 115 attr = self._set(name, value) 116 if attr.assignments is None: 117 attr.assignments = 1 118 attr.assignment_values.add(attr.value) 119 120 def _set(self, name, value): 121 122 "The underlying set operation associating 'name' with 'value'." 123 124 if not self.namespace.has_key(name): 125 126 # Either accept the attribute as specified. 127 128 if isinstance(value, Attr): 129 if value.context is not None: 130 self.namespace[name] = Attr(None, self, value.context, name, value.value) 131 return self.namespace[name] 132 else: 133 value = value.value 134 135 # Or attempt to fix the context. 136 137 context = self._context(value) 138 self.namespace[name] = Attr(None, self, context, name, value) 139 140 return self.namespace[name] 141 142 def _context(self, value): 143 144 """ 145 Return the context to be used when storing the given 'value'. 146 NOTE: This context is not likely to be useful when preparing an image 147 NOTE: since only instance contexts have significant effects at run-time. 148 """ 149 150 return None 151 152 def make_global(self, name): 153 if not self.namespace.has_key(name): 154 self.globals.add(name) 155 else: 156 raise InspectError(self.full_name(), self.astnode, "Name %r is both global and local in %r" % (name, self.full_name())) 157 158 def get_assignments(self, name): 159 if self.assignments.has_key(name): 160 return max(self.assignments[name], len(self.assignment_values[name])) 161 else: 162 return None 163 164 def attributes_as_list(self): 165 166 "Return the attributes in a list." 167 168 self.finalise_attributes() 169 l = [None] * len(self.keys()) 170 for attr in self.values(): 171 l[attr.position] = attr 172 return l 173 174 def finalise_attributes(self): 175 176 "Make sure all attributes are fully defined." 177 178 if self.finalised: 179 return 180 181 # The default action is to assign attribute positions sequentially. 182 183 for i, attr in enumerate(self.values()): 184 attr.position = i 185 186 self.finalised = 1 187 188 # Program data structures. There are two separate kinds of structures: those 189 # with context, which are the values manipulated by programs, and those without 190 # context, which are typically constant things which are stored alongside the 191 # program but which are wrapped in context-dependent structures in the running 192 # program. 193 194 class Attr: 195 196 "An attribute entry having a context." 197 198 def __init__(self, position, parent, context, name, value=None, assignments=None): 199 200 """ 201 Initialise the attribute with the given 'position' within the collection 202 of attributes of its 'parent', indicating the 'context' or origin of the 203 attribute (where it was first defined), along with its 'name'. 204 205 An optional 'value' indicates the typical contents of the attribute, and 206 the optional number of 'assignments' may be used to determine whether 207 the attribute is effectively constant. 208 """ 209 210 self.position = position 211 self.parent = parent 212 self.context = context 213 self.name = name 214 self.value = value 215 216 # Number of assignments per name. 217 218 self.assignments = assignments 219 self.assignment_values = set() 220 221 def set_referenced(self): 222 223 "Indicate that the contents are referenced via a namespace." 224 225 for value in self.assignment_values: 226 value.set_referenced() 227 228 def update(self, value, single_assignment): 229 230 """ 231 Update the attribute, adding the 'value' provided to the known values 232 associated with the attribute, changing the number of assignments 233 according to the 'single_assignment' status of the operation, where 234 a true value indicates that only one assignment is associated with the 235 update, and a false value indicates that potentially many assignments 236 may be involved. 237 """ 238 239 if self.assignments is None: 240 if single_assignment: 241 self.assignments = 1 242 else: 243 self.assignments = AtLeast(1) 244 else: 245 if single_assignment: 246 self.assignments += 1 247 else: 248 self.assignments += AtLeast(1) 249 250 if value is not None: 251 self.assignment_values.add(value) 252 253 def via_instance(self): 254 255 """ 256 Return either this attribute or a replacement where it is being accessed 257 via an instance. 258 """ 259 260 if self.context is not None: 261 262 # Check compatibility of the context with the parent. 263 # Where the attribute originates within the same hierarchy, use an 264 # instance as the context. 265 266 if self.defined_within_hierarchy(): 267 context = Instance() 268 269 # Otherwise, preserve the existing context. 270 271 else: 272 context = self.context 273 274 return Attr(self.position, self.parent, context, self.name, self.value, self.assignments) 275 276 # Unknown contexts remain in use. 277 278 else: 279 return self 280 281 def is_class_attribute(self): 282 return isinstance(self.parent, Class) 283 284 def defined_within_hierarchy(self): 285 286 """ 287 Return whether the parent and context of the attribute belong to the 288 same class hierarchy. 289 """ 290 291 return isinstance(self.parent, Class) and isinstance(self.context, Class) and ( 292 self.context is self.parent or 293 self.context.has_subclass(self.parent) or 294 self.parent.has_subclass(self.context)) 295 296 def __repr__(self): 297 return "Attr(%r, %s, %s, %r, %s, %r)" % ( 298 self.position, shortrepr(self.parent), shortrepr(self.context), 299 self.name, shortrepr(self.value), self.assignments 300 ) 301 302 # Instances are special in that they need to be wrapped together with context in 303 # a running program, but they are not generally constant. 304 305 class Instance: 306 307 "A placeholder indicating the involvement of an instance." 308 309 def __init__(self): 310 self.parent = None 311 self.referenced = 0 312 313 # Image generation details. 314 315 self.location = None 316 317 def set_referenced(self): 318 self.referenced = 1 319 320 def __repr__(self): 321 return "Instance()" 322 323 __shortrepr__ = __repr__ 324 325 class Constant: 326 327 "A superclass for all constant or context-free structures." 328 329 pass 330 331 # Data objects appearing in programs before run-time. 332 333 class Const(Constant, Instance): 334 335 "A constant object with no context." 336 337 def __init__(self, value): 338 Instance.__init__(self) 339 self.value = value 340 341 def __repr__(self): 342 if self.location is not None: 343 return "Const(%r, location=%r)" % (self.value, self.location) 344 else: 345 return "Const(%r)" % self.value 346 347 __shortrepr__ = __repr__ 348 349 # Support constants as dictionary keys in order to build constant tables. 350 351 def __eq__(self, other): 352 return self.value == other.value 353 354 def __hash__(self): 355 return hash(self.value) 356 357 def value_type_name(self): 358 return "__builtins__." + self.value.__class__.__name__ 359 360 class Class(NamespaceDict, Naming, Constant): 361 362 "An inspected class." 363 364 def __init__(self, name, parent, global_namespace=None, node=None): 365 366 """ 367 Initialise the class with the given 'name', 'parent' object, optional 368 'global_namespace' and optional AST 'node'. 369 """ 370 371 NamespaceDict.__init__(self, global_namespace) 372 self.name = name 373 self.parent = parent 374 self.astnode = node 375 self.referenced = 0 376 377 # Superclasses, descendants and attributes. 378 379 self.bases = [] 380 self.descendants = set() 381 self.instattr = set() # instance attributes 382 self.relocated = set() # attributes which do not have the same 383 # position as those of the same name in 384 # some superclasses 385 386 # Caches. 387 388 self.all_instattr = None # cache for instance_attributes 389 self.all_instattr_names = None # from all_instattr 390 self.all_classattr = None # cache for all_class_attributes 391 self.all_classattr_names = None # from all_classattr 392 self.allattr = None # cache for all_attributes 393 self.allattr_names = None # from allattr 394 395 # Add this class to its attributes. 396 397 self.set("__class__", self) 398 399 # Image generation details. 400 401 self.location = None 402 self.code_location = None 403 self.instantiator = None 404 405 # Program-related details. 406 407 self.temp_usage = 0 408 self.local_usage = 0 409 self.all_local_usage = 0 410 411 def set_referenced(self): 412 self.referenced = 1 413 414 def __repr__(self): 415 if self.location is not None: 416 return "Class(%r, %s, location=%r)" % (self.name, shortrepr(self.parent), self.location) 417 else: 418 return "Class(%r, %s)" % (self.name, shortrepr(self.parent)) 419 420 def __shortrepr__(self): 421 return "Class(%r, %s)" % (self.name, shortrepr(self.parent)) 422 423 def _context(self, value): 424 425 """ 426 Return the context to be used when storing the given 'value'. 427 NOTE: This context is not likely to be useful when preparing an image 428 NOTE: since only instance contexts have significant effects at run-time. 429 """ 430 431 if value is not None: 432 context = value.parent 433 if isinstance(context, Module): 434 return self 435 else: 436 return context 437 else: 438 return None 439 440 def finalise_attributes(self): 441 442 "Make sure that all attributes are fully defined." 443 444 if self.finalised: 445 return 446 447 self.finalise_class_attributes() 448 self.finalise_instance_attributes() 449 self.finalised = 1 450 451 def get_instantiator(self): 452 453 "Return a function which can be used to instantiate the class." 454 455 if self.instantiator is None: 456 self.instantiator = self.get_init_method().function_from_method() 457 return self.instantiator 458 459 def get_init_method(self): 460 return self.all_class_attributes()["__init__"].value 461 462 # Class-specific methods. 463 464 def add_base(self, base): 465 self.bases.append(base) 466 base.add_descendant(self) 467 468 def add_instance_attribute(self, name): 469 self.instattr.add(name) 470 471 def add_descendant(self, cls): 472 self.descendants.add(cls) 473 for base in self.bases: 474 base.add_descendant(cls) 475 476 def has_subclass(self, other): 477 return other in self.descendants 478 479 def all_descendants(self): 480 d = {} 481 for cls in self.descendants: 482 d[cls.full_name()] = cls 483 return d 484 485 "Return the attribute names provided by this class only." 486 487 class_attribute_names = NamespaceDict.keys 488 489 def class_attributes(self): 490 491 "Return class attributes provided by this class only." 492 493 return dict(self) 494 495 def all_class_attribute_names(self): 496 497 "Return the attribute names provided by classes in this hierarchy." 498 499 if self.all_classattr_names is None: 500 self.all_class_attributes() 501 return self.all_classattr_names 502 503 def all_class_attributes(self): 504 505 "Return all class attributes, indicating the class which provides them." 506 507 self.finalise_class_attributes() 508 return self.all_classattr 509 510 def finalise_class_attributes(self): 511 512 "Make sure that the class attributes are fully defined." 513 514 if self.all_classattr is None: 515 self.all_classattr = {} 516 clsattr = {} 517 518 # Record provisional position information for attributes of this 519 # class. 520 521 for name in self.class_attributes().keys(): 522 clsattr[name] = set() # position not yet defined 523 524 reversed_bases = self.bases[:] 525 reversed_bases.reverse() 526 527 # For the bases in reverse order, acquire class attribute details. 528 529 for cls in reversed_bases: 530 for name, attr in cls.all_class_attributes().items(): 531 self.all_classattr[name] = attr 532 533 # Record previous attribute information. 534 535 if clsattr.has_key(name): 536 clsattr[name].add(attr.position) 537 538 # Record class attributes provided by this class and its bases, 539 # along with their positions. 540 541 self.all_classattr.update(self.class_attributes()) 542 543 if clsattr: 544 for i, name in enumerate(self._get_position_list(clsattr)): 545 self.all_classattr[name].position = i 546 547 return self.all_classattr 548 549 def instance_attribute_names(self): 550 551 "Return the instance attribute names provided by the class." 552 553 if self.all_instattr_names is None: 554 self.instance_attributes() 555 return self.all_instattr_names 556 557 def instance_attributes(self): 558 559 "Return instance-only attributes for instances of this class." 560 561 self.finalise_instance_attributes() 562 return self.all_instattr 563 564 def finalise_instance_attributes(self): 565 566 "Make sure that the instance attributes are fully defined." 567 568 if self.all_instattr is None: 569 self.all_instattr = {} 570 instattr = {} 571 572 # Record provisional position information for attributes of this 573 # instance. 574 575 for name in self.instattr: 576 instattr[name] = set() # position not yet defined 577 578 reversed_bases = self.bases[:] 579 reversed_bases.reverse() 580 581 # For the bases in reverse order, acquire instance attribute 582 # details. 583 584 for cls in reversed_bases: 585 for name, attr in cls.instance_attributes().items(): 586 587 # Record previous attribute information. 588 589 if instattr.has_key(name): 590 instattr[name].add(attr.position) 591 592 # Cache the attributes by converting the positioned attributes into 593 # a dictionary. 594 595 if not instattr: 596 self.all_instattr = {} 597 else: 598 self.all_instattr = self._get_attributes(instattr) 599 600 self.all_instattr_names = self.all_instattr.keys() 601 602 return self.all_instattr 603 604 def _get_position_list(self, positions): 605 606 """ 607 Return a list of attribute names for the given 'positions' mapping from 608 names to positions, indicating the positions of the attributes in the 609 final instance structure. 610 """ 611 612 position_items = positions.items() 613 namearray = [None] * len(position_items) 614 615 # Get the positions in ascending order of list size, with lists 616 # of the same size ordered according to their smallest position 617 # value. 618 619 position_items.sort(self._cmp_positions) 620 621 # Get the names in position order. 622 623 held = [] 624 625 for name, pos in position_items: 626 pos = list(pos) 627 pos.sort() 628 if pos and pos[0] < len(namearray) and namearray[pos[0]] is None: 629 namearray[pos[0]] = name 630 else: 631 if pos: 632 self.relocated.add(name) 633 held.append((name, pos)) 634 635 for i, attr in enumerate(namearray): 636 if attr is None: 637 name, pos = held.pop() 638 namearray[i] = name 639 640 #print self.name, positions 641 #print "->", namearray 642 return namearray 643 644 def _get_attributes(self, positions): 645 646 """ 647 For the given 'positions' mapping from names to positions, return a 648 dictionary mapping names to Attr instances incorporating information 649 about their positions in the final instance structure. 650 """ 651 652 d = {} 653 for i, name in enumerate(self._get_position_list(positions)): 654 d[name] = Attr(i, Instance(), None, name, None) 655 return d 656 657 def _cmp_positions(self, a, b): 658 659 "Compare name plus position list operands 'a' and 'b'." 660 661 name_a, list_a = a 662 name_b, list_b = b 663 if len(list_a) < len(list_b): 664 return -1 665 elif len(list_a) > len(list_b): 666 return 1 667 elif not list_a: 668 return 0 669 else: 670 return cmp(min(list_a), min(list_b)) 671 672 def all_attribute_names(self): 673 674 """ 675 Return the names of all attributes provided by instances of this class. 676 """ 677 678 self.allattr_names = self.allattr_names or self.all_attributes().keys() 679 return self.allattr_names 680 681 def all_attributes(self): 682 683 """ 684 Return all attributes for an instance, indicating either the class which 685 provides them or that the instance itself provides them. 686 """ 687 688 if self.allattr is None: 689 self.allattr = {} 690 self.allattr.update(self.all_class_attributes()) 691 for name, attr in self.instance_attributes().items(): 692 if self.allattr.has_key(name): 693 print "Instance attribute %r in %r overrides class attribute." % (name, self) 694 self.allattr[name] = attr 695 return self.allattr 696 697 class Function(NamespaceDict, Naming, Constant): 698 699 "An inspected function." 700 701 def __init__(self, name, parent, argnames, defaults, has_star, has_dstar, global_namespace=None, node=None): 702 703 """ 704 Initialise the function with the given 'name', 'parent', list of 705 'argnames', list of 'defaults', the 'has_star' flag (indicating the 706 presence of a * parameter), the 'has_dstar' flag (indicating the 707 presence of a ** parameter), optional 'global_namespace', and optional 708 AST 'node'. 709 """ 710 711 NamespaceDict.__init__(self, global_namespace) 712 self.name = name 713 self.parent = parent 714 self.argnames = argnames 715 self.defaults = defaults 716 self.has_star = has_star 717 self.has_dstar = has_dstar 718 self.astnode = node 719 self.referenced = 0 720 721 # Initialise the positional names. 722 723 self.positional_names = self.argnames[:] 724 if has_dstar: 725 self.dstar_name = self.positional_names[-1] 726 del self.positional_names[-1] 727 if has_star: 728 self.star_name = self.positional_names[-1] 729 del self.positional_names[-1] 730 731 # Initialise default storage. 732 # NOTE: This must be initialised separately due to the reliance on node 733 # NOTE: visiting. 734 735 self.default_attrs = [] 736 737 # Caches. 738 739 self.localnames = None # cache for locals 740 741 # Add parameters to the namespace. 742 743 self._add_parameters(argnames) 744 745 # Image generation details. 746 747 self.location = None 748 self.code_location = None 749 750 # Program-related details. 751 752 self.temp_usage = 0 753 self.local_usage = 0 754 self.all_local_usage = 0 755 756 def set_referenced(self): 757 self.referenced = 1 758 759 def _add_parameters(self, argnames): 760 for name in argnames: 761 if isinstance(name, tuple): 762 self._add_parameters(name) 763 else: 764 self.set(name, None) 765 766 def __repr__(self): 767 if self.location is not None: 768 return "Function(%r, %s, %r, location=%r, code_location=%r)" % ( 769 self.name, shortrepr(self.parent), self.argnames, self.location, self.code_location 770 ) 771 else: 772 return "Function(%r, %s, %r)" % ( 773 self.name, shortrepr(self.parent), self.argnames 774 ) 775 776 def __shortrepr__(self): 777 return "Function(%r, %s)" % ( 778 self.name, shortrepr(self.parent) 779 ) 780 781 def store_default(self, value): 782 attr = Attr(None, self, None, None, value) 783 attr.update(value, 1) 784 self.default_attrs.append(attr) 785 786 def make_global(self, name): 787 if name not in self.argnames and not self.has_key(name): 788 self.globals.add(name) 789 else: 790 raise InspectError(self.full_name(), self.astnode, "Name %r is global and local in %r" % (name, self.full_name())) 791 792 def parameters(self): 793 794 """ 795 Return a dictionary mapping parameter names to their position in the 796 parameter list. 797 """ 798 799 parameters = {} 800 for i, name in enumerate(self.argnames): 801 parameters[name] = i 802 return parameters 803 804 def all_locals(self): 805 806 "Return a dictionary mapping names to local and parameter details." 807 808 return dict(self) 809 810 def locals(self): 811 812 "Return a dictionary mapping names to local details." 813 814 if self.localnames is None: 815 self.localnames = {} 816 self.localnames.update(self.all_locals()) 817 for name in self.argnames: 818 del self.localnames[name] 819 return self.localnames 820 821 def is_method(self): 822 823 "Return whether this function is a method." 824 825 return isinstance(self.parent, Class) 826 827 def is_relocated(self, name): 828 829 """ 830 Determine whether the given attribute 'name' is relocated for instances 831 having this function as a method. 832 """ 833 834 for cls in self.parent.descendants: 835 if name in cls.relocated: 836 return 1 837 return 0 838 839 def finalise_attributes(self): 840 841 """ 842 Make sure all attributes (locals) are fully defined. Note that locals 843 are not attributes in the sense of class, module or instance attributes. 844 Defaults are also finalised by this method. 845 """ 846 847 for i, default in enumerate(self.default_attrs): 848 default.position = i 849 850 i = None 851 for i, name in enumerate(self.argnames): 852 self[name].position = i 853 854 if i is not None: 855 nparams = i + 1 856 else: 857 nparams = 0 858 859 i = None 860 for i, attr in enumerate(self.locals().values()): 861 attr.position = i + nparams 862 863 if i is not None: 864 nothers = i + 1 865 else: 866 nothers = 0 867 868 self.local_usage = nothers 869 self.all_local_usage = nparams + nothers 870 self.finalised = 1 871 872 def function_from_method(self): 873 874 "Make a function from a method." 875 876 function = Function(self.name, self.parent, self.argnames[1:], self.defaults, 877 self.has_star, self.has_dstar, self.global_namespace, self.astnode) 878 function.default_attrs = self.default_attrs 879 return function 880 881 class UnresolvedName(NamespaceDict, Constant): 882 883 "A module, class or function which was mentioned but could not be imported." 884 885 def __init__(self, name, parent_name, global_namespace=None): 886 NamespaceDict.__init__(self, global_namespace) 887 self.name = name 888 self.parent_name = parent_name 889 self.parent = None 890 self.referenced = 0 891 892 self.descendants = set() 893 894 def set_referenced(self): 895 self.referenced = 1 896 897 def add_descendant(self, cls): 898 self.descendants.add(cls) 899 900 def all_class_attributes(self): 901 return {} 902 903 def instance_attributes(self): 904 return {} 905 906 def __repr__(self): 907 return "UnresolvedName(%r, %r)" % (self.name, self.parent_name) 908 909 __shortrepr__ = __repr__ 910 911 def full_name(self): 912 if self.name is not None: 913 return self.parent_name + "." + self.name 914 else: 915 return self.parent_name 916 917 class Module(NamespaceDict, Constant): 918 919 "An inspected module's core details." 920 921 def __init__(self, name): 922 NamespaceDict.__init__(self, self) 923 self.name = name 924 self.parent = None 925 self.referenced = 0 926 927 # Original location details. 928 929 self.astnode = None 930 931 # Complete lists of classes and functions. 932 933 self.all_objects = set() 934 935 # Keyword records. 936 937 self.keyword_names = set() 938 939 # Image generation details. 940 941 self.location = None 942 self.code_location = None 943 944 # Program-related details. 945 946 self.temp_usage = 0 947 self.local_usage = 0 948 self.all_local_usage = 0 949 950 def set_referenced(self): 951 self.referenced = 1 952 953 def full_name(self): 954 return self.name 955 956 def __repr__(self): 957 if self.location is not None: 958 return "Module(%r, location=%r)" % (self.name, self.location) 959 else: 960 return "Module(%r)" % self.name 961 962 def __shortrepr__(self): 963 return "Module(%r)" % self.name 964 965 # Attribute methods. 966 967 "Return the module attribute names provided by the module." 968 969 module_attribute_names = NamespaceDict.keys 970 971 def module_attributes(self): 972 973 "Return a dictionary mapping names to module attributes." 974 975 return dict(self) 976 977 # vim: tabstop=4 expandtab shiftwidth=4