@@ -404,10 +404,37 @@ def test_findsource_code_in_linecache(self):
404404 self .assertEqual (inspect .findsource (co ), (lines ,0 ))
405405 self .assertEqual (inspect .getsource (co ), lines [0 ])
406406
407+
408+ class _BrokenDataDescriptor (object ):
409+ """
410+ A broken data descriptor. See bug #1785.
411+ """
412+ def __get__ (* args ):
413+ raise AssertionError ("should not __get__ data descriptors" )
414+
415+ def __set__ (* args ):
416+ raise RuntimeError
417+
418+ def __getattr__ (* args ):
419+ raise AssertionError ("should not __getattr__ data descriptors" )
420+
421+
422+ class _BrokenMethodDescriptor (object ):
423+ """
424+ A broken method descriptor. See bug #1785.
425+ """
426+ def __get__ (* args ):
427+ raise AssertionError ("should not __get__ method descriptors" )
428+
429+ def __getattr__ (* args ):
430+ raise AssertionError ("should not __getattr__ method descriptors" )
431+
432+
407433# Helper for testing classify_class_attrs.
408434def attrs_wo_objs (cls ):
409435 return [t [:3 ] for t in inspect .classify_class_attrs (cls )]
410436
437+
411438class TestClassesAndFunctions (unittest .TestCase ):
412439 def test_classic_mro (self ):
413440 # Test classic-class method resolution order.
@@ -494,13 +521,18 @@ def m1(self): pass
494521
495522 datablob = '1'
496523
524+ dd = _BrokenDataDescriptor ()
525+ md = _BrokenMethodDescriptor ()
526+
497527 attrs = attrs_wo_objs (A )
498528 self .assertIn (('s' , 'static method' , A ), attrs , 'missing static method' )
499529 self .assertIn (('c' , 'class method' , A ), attrs , 'missing class method' )
500530 self .assertIn (('p' , 'property' , A ), attrs , 'missing property' )
501531 self .assertIn (('m' , 'method' , A ), attrs , 'missing plain method' )
502532 self .assertIn (('m1' , 'method' , A ), attrs , 'missing plain method' )
503533 self .assertIn (('datablob' , 'data' , A ), attrs , 'missing data' )
534+ self .assertIn (('md' , 'method' , A ), attrs , 'missing method descriptor' )
535+ self .assertIn (('dd' , 'data' , A ), attrs , 'missing data descriptor' )
504536
505537 class B (A ):
506538 def m (self ): pass
@@ -512,6 +544,8 @@ def m(self): pass
512544 self .assertIn (('m' , 'method' , B ), attrs , 'missing plain method' )
513545 self .assertIn (('m1' , 'method' , A ), attrs , 'missing plain method' )
514546 self .assertIn (('datablob' , 'data' , A ), attrs , 'missing data' )
547+ self .assertIn (('md' , 'method' , A ), attrs , 'missing method descriptor' )
548+ self .assertIn (('dd' , 'data' , A ), attrs , 'missing data descriptor' )
515549
516550
517551 class C (A ):
@@ -525,6 +559,8 @@ def c(self): pass
525559 self .assertIn (('m' , 'method' , C ), attrs , 'missing plain method' )
526560 self .assertIn (('m1' , 'method' , A ), attrs , 'missing plain method' )
527561 self .assertIn (('datablob' , 'data' , A ), attrs , 'missing data' )
562+ self .assertIn (('md' , 'method' , A ), attrs , 'missing method descriptor' )
563+ self .assertIn (('dd' , 'data' , A ), attrs , 'missing data descriptor' )
528564
529565 class D (B , C ):
530566 def m1 (self ): pass
@@ -539,6 +575,8 @@ def m1(self): pass
539575 self .assertIn (('m' , 'method' , B ), attrs , 'missing plain method' )
540576 self .assertIn (('m1' , 'method' , D ), attrs , 'missing plain method' )
541577 self .assertIn (('datablob' , 'data' , A ), attrs , 'missing data' )
578+ self .assertIn (('md' , 'method' , A ), attrs , 'missing method descriptor' )
579+ self .assertIn (('dd' , 'data' , A ), attrs , 'missing data descriptor' )
542580
543581
544582 def test_classify_oldstyle (self ):
@@ -554,6 +592,64 @@ def test_classify_newstyle(self):
554592 """
555593 self ._classify_test (True )
556594
595+ def test_classify_builtin_types (self ):
596+ # Simple sanity check that all built-in types can have their
597+ # attributes classified.
598+ for name in dir (__builtin__ ):
599+ builtin = getattr (__builtin__ , name )
600+ if isinstance (builtin , type ):
601+ inspect .classify_class_attrs (builtin )
602+
603+ def test_getmembers_descriptors (self ):
604+ # Old-style classes
605+ class A :
606+ dd = _BrokenDataDescriptor ()
607+ md = _BrokenMethodDescriptor ()
608+
609+ self .assertEqual (inspect .getmembers (A , inspect .ismethoddescriptor ),
610+ [('md' , A .__dict__ ['md' ])])
611+ self .assertEqual (inspect .getmembers (A , inspect .isdatadescriptor ),
612+ [('dd' , A .__dict__ ['dd' ])])
613+
614+ class B (A ):
615+ pass
616+
617+ self .assertEqual (inspect .getmembers (B , inspect .ismethoddescriptor ),
618+ [('md' , A .__dict__ ['md' ])])
619+ self .assertEqual (inspect .getmembers (B , inspect .isdatadescriptor ),
620+ [('dd' , A .__dict__ ['dd' ])])
621+
622+ # New-style classes
623+ class A (object ):
624+ dd = _BrokenDataDescriptor ()
625+ md = _BrokenMethodDescriptor ()
626+
627+ def pred_wrapper (pred ):
628+ # A quick'n'dirty way to discard standard attributes of new-style
629+ # classes.
630+ class Empty (object ):
631+ pass
632+ def wrapped (x ):
633+ if hasattr (x , '__name__' ) and hasattr (Empty , x .__name__ ):
634+ return False
635+ return pred (x )
636+ return wrapped
637+
638+ ismethoddescriptor = pred_wrapper (inspect .ismethoddescriptor )
639+ isdatadescriptor = pred_wrapper (inspect .isdatadescriptor )
640+
641+ self .assertEqual (inspect .getmembers (A , ismethoddescriptor ),
642+ [('md' , A .__dict__ ['md' ])])
643+ self .assertEqual (inspect .getmembers (A , isdatadescriptor ),
644+ [('dd' , A .__dict__ ['dd' ])])
645+
646+ class B (A ):
647+ pass
648+
649+ self .assertEqual (inspect .getmembers (B , ismethoddescriptor ),
650+ [('md' , A .__dict__ ['md' ])])
651+ self .assertEqual (inspect .getmembers (B , isdatadescriptor ),
652+ [('dd' , A .__dict__ ['dd' ])])
557653
558654
559655class TestGetcallargsFunctions (unittest .TestCase ):
0 commit comments