Return list of attribute-descriptor tuples. For each name in dir(cls), the return list contains a 4-tuple with these elements: 0. The name (a string). 1. The kind of attribute this is, one of these strings: 'class method' created via classmethod()
(cls)
| 611 | Attribute = namedtuple('Attribute', 'name kind defining_class object') |
| 612 | |
| 613 | def classify_class_attrs(cls): |
| 614 | """Return list of attribute-descriptor tuples. |
| 615 | |
| 616 | For each name in dir(cls), the return list contains a 4-tuple |
| 617 | with these elements: |
| 618 | |
| 619 | 0. The name (a string). |
| 620 | |
| 621 | 1. The kind of attribute this is, one of these strings: |
| 622 | 'class method' created via classmethod() |
| 623 | 'static method' created via staticmethod() |
| 624 | 'property' created via property() |
| 625 | 'method' any other flavor of method or descriptor |
| 626 | 'data' not a method |
| 627 | |
| 628 | 2. The class which defined this attribute (a class). |
| 629 | |
| 630 | 3. The object as obtained by calling getattr; if this fails, or if the |
| 631 | resulting object does not live anywhere in the class' mro (including |
| 632 | metaclasses) then the object is looked up in the defining class's |
| 633 | dict (found by walking the mro). |
| 634 | |
| 635 | If one of the items in dir(cls) is stored in the metaclass it will now |
| 636 | be discovered and not have None be listed as the class in which it was |
| 637 | defined. Any items whose home class cannot be discovered are skipped. |
| 638 | """ |
| 639 | |
| 640 | mro = getmro(cls) |
| 641 | metamro = getmro(type(cls)) # for attributes stored in the metaclass |
| 642 | metamro = tuple(cls for cls in metamro if cls not in (type, object)) |
| 643 | class_bases = (cls,) + mro |
| 644 | all_bases = class_bases + metamro |
| 645 | names = dir(cls) |
| 646 | # :dd any DynamicClassAttributes to the list of names; |
| 647 | # this may result in duplicate entries if, for example, a virtual |
| 648 | # attribute with the same name as a DynamicClassAttribute exists. |
| 649 | for base in mro: |
| 650 | for k, v in base.__dict__.items(): |
| 651 | if isinstance(v, types.DynamicClassAttribute) and v.fget is not None: |
| 652 | names.append(k) |
| 653 | result = [] |
| 654 | processed = set() |
| 655 | |
| 656 | for name in names: |
| 657 | # Get the object associated with the name, and where it was defined. |
| 658 | # Normal objects will be looked up with both getattr and directly in |
| 659 | # its class' dict (in case getattr fails [bug #1785], and also to look |
| 660 | # for a docstring). |
| 661 | # For DynamicClassAttributes on the second pass we only look in the |
| 662 | # class's dict. |
| 663 | # |
| 664 | # Getting an obj from the __dict__ sometimes reveals more than |
| 665 | # using getattr. Static and class methods are dramatic examples. |
| 666 | homecls = None |
| 667 | get_obj = None |
| 668 | dict_obj = None |
| 669 | if name not in processed: |
| 670 | try: |