@@ -430,8 +430,116 @@ def g():
430430 self .assertTrue ("maximum recursion depth exceeded" in str (v ), v )
431431
432432
433+
434+ # Helper class used by TestSameStrAndUnicodeMsg
435+ class ExcWithOverriddenStr (Exception ):
436+ """Subclass of Exception that accepts a keyword 'msg' arg that is
437+ returned by __str__. 'msg' won't be included in self.args"""
438+ def __init__ (self , * args , ** kwargs ):
439+ self .msg = kwargs .pop ('msg' ) # msg should always be present
440+ super (ExcWithOverriddenStr , self ).__init__ (* args , ** kwargs )
441+ def __str__ (self ):
442+ return self .msg
443+
444+
445+ class TestSameStrAndUnicodeMsg (unittest .TestCase ):
446+ """unicode(err) should return the same message of str(err). See #6108"""
447+
448+ def check_same_msg (self , exc , msg ):
449+ """Helper function that checks if str(exc) == unicode(exc) == msg"""
450+ self .assertEqual (str (exc ), msg )
451+ self .assertEqual (str (exc ), unicode (exc ))
452+
453+ def test_builtin_exceptions (self ):
454+ """Check same msg for built-in exceptions"""
455+ # These exceptions implement a __str__ method that uses the args
456+ # to create a better error message. unicode(e) should return the same
457+ # message.
458+ exceptions = [
459+ SyntaxError ('invalid syntax' , ('<string>' , 1 , 3 , '2+*3' )),
460+ IOError (2 , 'No such file or directory' ),
461+ KeyError ('both should have the same quotes' ),
462+ UnicodeDecodeError ('ascii' , '\xc3 \xa0 ' , 0 , 1 ,
463+ 'ordinal not in range(128)' ),
464+ UnicodeEncodeError ('ascii' , u'\u1234 ' , 0 , 1 ,
465+ 'ordinal not in range(128)' )
466+ ]
467+ for exception in exceptions :
468+ self .assertEqual (str (exception ), unicode (exception ))
469+
470+ def test_0_args (self ):
471+ """Check same msg for Exception with 0 args"""
472+ # str() and unicode() on an Exception with no args should return an
473+ # empty string
474+ self .check_same_msg (Exception (), '' )
475+
476+ def test_0_args_with_overridden___str__ (self ):
477+ """Check same msg for exceptions with 0 args and overridden __str__"""
478+ # str() and unicode() on an exception with overridden __str__ that
479+ # returns an ascii-only string should return the same string
480+ for msg in ('foo' , u'foo' ):
481+ self .check_same_msg (ExcWithOverriddenStr (msg = msg ), msg )
482+
483+ # if __str__ returns a non-ascii unicode string str() should fail
484+ # but unicode() should return the unicode string
485+ e = ExcWithOverriddenStr (msg = u'f\xf6 \xf6 ' ) # no args
486+ self .assertRaises (UnicodeEncodeError , str , e )
487+ self .assertEqual (unicode (e ), u'f\xf6 \xf6 ' )
488+
489+ def test_1_arg (self ):
490+ """Check same msg for Exceptions with 1 arg"""
491+ for arg in ('foo' , u'foo' ):
492+ self .check_same_msg (Exception (arg ), arg )
493+
494+ # if __str__ is not overridden and self.args[0] is a non-ascii unicode
495+ # string, str() should try to return str(self.args[0]) and fail.
496+ # unicode() should return unicode(self.args[0]) and succeed.
497+ e = Exception (u'f\xf6 \xf6 ' )
498+ self .assertRaises (UnicodeEncodeError , str , e )
499+ self .assertEqual (unicode (e ), u'f\xf6 \xf6 ' )
500+
501+ def test_1_arg_with_overridden___str__ (self ):
502+ """Check same msg for exceptions with overridden __str__ and 1 arg"""
503+ # when __str__ is overridden and __unicode__ is not implemented
504+ # unicode(e) returns the same as unicode(e.__str__()).
505+ for msg in ('foo' , u'foo' ):
506+ self .check_same_msg (ExcWithOverriddenStr ('arg' , msg = msg ), msg )
507+
508+ # if __str__ returns a non-ascii unicode string, str() should fail
509+ # but unicode() should succeed.
510+ e = ExcWithOverriddenStr ('arg' , msg = u'f\xf6 \xf6 ' ) # 1 arg
511+ self .assertRaises (UnicodeEncodeError , str , e )
512+ self .assertEqual (unicode (e ), u'f\xf6 \xf6 ' )
513+
514+ def test_many_args (self ):
515+ """Check same msg for Exceptions with many args"""
516+ argslist = [
517+ (3 , 'foo' ),
518+ (1 , u'foo' , 'bar' ),
519+ (4 , u'f\xf6 \xf6 ' , u'bar' , 'baz' )
520+ ]
521+ # both str() and unicode() should return a repr() of the args
522+ for args in argslist :
523+ self .check_same_msg (Exception (* args ), repr (args ))
524+
525+ def test_many_args_with_overridden___str__ (self ):
526+ """Check same msg for exceptions with overridden __str__ and many args"""
527+ # if __str__ returns an ascii string / ascii unicode string
528+ # both str() and unicode() should succeed
529+ for msg in ('foo' , u'foo' ):
530+ e = ExcWithOverriddenStr ('arg1' , u'arg2' , u'f\xf6 \xf6 ' , msg = msg )
531+ self .check_same_msg (e , msg )
532+
533+ # if __str__ returns a non-ascii unicode string, str() should fail
534+ # but unicode() should succeed
535+ e = ExcWithOverriddenStr ('arg1' , u'f\xf6 \xf6 ' , u'arg3' , # 3 args
536+ msg = u'f\xf6 \xf6 ' )
537+ self .assertRaises (UnicodeEncodeError , str , e )
538+ self .assertEqual (unicode (e ), u'f\xf6 \xf6 ' )
539+
540+
433541def test_main ():
434- run_unittest (ExceptionTests )
542+ run_unittest (ExceptionTests , TestSameStrAndUnicodeMsg )
435543
436544if __name__ == '__main__' :
437545 test_main ()
0 commit comments