@@ -532,3 +532,136 @@ Traceback (most recent call last):
532532 File "native.py", line 6, in <module>
533533 f(y)
534534TypeError: int object expected; got str
535+
536+ [case testRaiseFrom]
537+ from typing import Any
538+
539+ from exceptions_helper import OtherException
540+ from testutil import assertRaises
541+
542+ class Inner(Exception):
543+ pass
544+
545+ class Outer(Exception):
546+ pass
547+
548+ def raise_from(exc: Any, cause: Any) -> None:
549+ try:
550+ raise Inner("inner")
551+ except Inner:
552+ raise exc from cause
553+
554+ def test_raise_from_instance() -> None:
555+ cause = ValueError("cause")
556+ try:
557+ raise_from(Outer("outer"), cause)
558+ except Outer as e:
559+ assert e.__cause__ is cause
560+ assert e.__suppress_context__
561+ assert isinstance(e.__context__, Inner)
562+ else:
563+ assert False
564+
565+ def test_raise_from_class() -> None:
566+ try:
567+ raise_from(Outer, ValueError)
568+ except Outer as e:
569+ assert type(e.__cause__) is ValueError
570+ assert e.__suppress_context__
571+ else:
572+ assert False
573+
574+ def test_raise_from_none() -> None:
575+ try:
576+ raise_from(Outer("outer"), None)
577+ except Outer as e:
578+ assert e.__cause__ is None
579+ assert e.__suppress_context__
580+ assert isinstance(e.__context__, Inner)
581+ else:
582+ assert False
583+
584+ def test_raise_from_invalid_cause() -> None:
585+ with assertRaises(TypeError, "exception causes must derive from BaseException"):
586+ raise_from(Outer("outer"), 1)
587+
588+ def test_raise_from_invalid_exception() -> None:
589+ with assertRaises(TypeError, "exceptions must derive from BaseException"):
590+ raise_from(1, ValueError())
591+
592+ def test_raise_from_in_except() -> None:
593+ try:
594+ try:
595+ raise Inner("inner")
596+ except Inner as exc:
597+ raise Outer("outer") from exc
598+ except Outer as e:
599+ assert isinstance(e.__cause__, Inner)
600+ assert e.__cause__ is e.__context__
601+ else:
602+ assert False
603+
604+ def test_raise_from_reassigned_exception() -> None:
605+ original = ValueError("original")
606+ cause = TypeError("cause")
607+ exc: Exception = original
608+ try:
609+ raise exc from (exc := cause)
610+ except ValueError as e:
611+ assert e is original
612+ assert e.__cause__ is cause
613+ assert exc is cause
614+ else:
615+ assert False
616+
617+ def test_raise_from_class_returning_unrelated_exception() -> None:
618+ # Normalization calls OtherException again with the ValueError as an argument.
619+ with assertRaises(TypeError):
620+ raise_from(OtherException, None)
621+
622+ def raise_plain(exc: Any) -> None:
623+ raise exc
624+
625+ def test_raise_plain_preserves_cause() -> None:
626+ cause = ValueError("cause")
627+ exc = Outer("outer")
628+ try:
629+ raise_from(exc, cause)
630+ except Outer:
631+ pass
632+ try:
633+ raise_plain(exc)
634+ except Outer as e:
635+ assert e is exc
636+ assert e.__cause__ is cause
637+ assert e.__suppress_context__
638+ else:
639+ assert False
640+
641+ def test_raise_plain_does_not_suppress_context() -> None:
642+ try:
643+ try:
644+ raise Inner("inner")
645+ except Inner:
646+ raise_plain(Outer("outer"))
647+ except Outer as e:
648+ assert e.__cause__ is None
649+ assert not e.__suppress_context__
650+ assert isinstance(e.__context__, Inner)
651+ else:
652+ assert False
653+
654+ def test_raise_plain_invalid_exception() -> None:
655+ with assertRaises(TypeError, "exceptions must derive from BaseException"):
656+ raise_plain(1)
657+
658+ def test_raise_plain_class_returning_unrelated_exception() -> None:
659+ with assertRaises(TypeError):
660+ raise_plain(OtherException)
661+
662+ [file exceptions_helper.py]
663+ from typing import Any
664+
665+ class OtherException(Exception):
666+ def __new__(cls) -> Any:
667+ return ValueError("other")
0 commit comments