Close issue #6210: Implement PEP 409
diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py
index a7683ac..91d85ef 100644
--- a/Lib/test/test_exceptions.py
+++ b/Lib/test/test_exceptions.py
@@ -387,19 +387,36 @@
 
     def testChainingAttrs(self):
         e = Exception()
-        self.assertEqual(e.__context__, None)
-        self.assertEqual(e.__cause__, None)
+        self.assertIsNone(e.__context__)
+        self.assertIs(e.__cause__, Ellipsis)
 
         e = TypeError()
-        self.assertEqual(e.__context__, None)
-        self.assertEqual(e.__cause__, None)
+        self.assertIsNone(e.__context__)
+        self.assertIs(e.__cause__, Ellipsis)
 
         class MyException(EnvironmentError):
             pass
 
         e = MyException()
-        self.assertEqual(e.__context__, None)
-        self.assertEqual(e.__cause__, None)
+        self.assertIsNone(e.__context__)
+        self.assertIs(e.__cause__, Ellipsis)
+
+    def testChainingDescriptors(self):
+        try:
+            raise Exception()
+        except Exception as exc:
+            e = exc
+
+        self.assertIsNone(e.__context__)
+        self.assertIs(e.__cause__, Ellipsis)
+
+        e.__context__ = NameError()
+        e.__cause__ = None
+        self.assertIsInstance(e.__context__, NameError)
+        self.assertIsNone(e.__cause__)
+
+        e.__cause__ = Ellipsis
+        self.assertIs(e.__cause__, Ellipsis)
 
     def testKeywordArgs(self):
         # test that builtin exception don't take keyword args,
diff --git a/Lib/test/test_raise.py b/Lib/test/test_raise.py
index 92c50c7..8ae9210 100644
--- a/Lib/test/test_raise.py
+++ b/Lib/test/test_raise.py
@@ -3,12 +3,27 @@
 
 """Tests for the raise statement."""
 
-from test import support
+from test import support, script_helper
+import re
 import sys
 import types
 import unittest
 
 
+try:
+    from resource import setrlimit, RLIMIT_CORE, error as resource_error
+except ImportError:
+    prepare_subprocess = None
+else:
+    def prepare_subprocess():
+        # don't create core file
+        try:
+            setrlimit(RLIMIT_CORE, (0, 0))
+        except (ValueError, resource_error):
+            pass
+
+
+
 def get_tb():
     try:
         raise OSError()
@@ -77,6 +92,16 @@
                 nested_reraise()
         self.assertRaises(TypeError, reraise)
 
+    def test_raise_from_None(self):
+        try:
+            try:
+                raise TypeError("foo")
+            except:
+                raise ValueError() from None
+        except ValueError as e:
+            self.assertTrue(isinstance(e.__context__, TypeError))
+            self.assertIsNone(e.__cause__)
+
     def test_with_reraise1(self):
         def reraise():
             try:
@@ -139,6 +164,23 @@
 
 
 class TestCause(unittest.TestCase):
+
+    def testCauseSyntax(self):
+        try:
+            try:
+                try:
+                    raise TypeError
+                except Exception:
+                    raise ValueError from None
+            except ValueError as exc:
+                self.assertIsNone(exc.__cause__)
+                raise exc from Ellipsis
+        except ValueError as exc:
+            e = exc
+
+        self.assertIs(e.__cause__, Ellipsis)
+        self.assertIsInstance(e.__context__, TypeError)
+
     def test_invalid_cause(self):
         try:
             raise IndexError from 5
@@ -178,6 +220,44 @@
 
 
 class TestTraceback(unittest.TestCase):
+
+    def get_output(self, code, filename=None):
+        """
+        Run the specified code in Python (in a new child process) and read the
+        output from the standard error or from a file (if filename is set).
+        Return the output lines as a list.
+        """
+        options = {}
+        if prepare_subprocess:
+            options['preexec_fn'] = prepare_subprocess
+        process = script_helper.spawn_python('-c', code, **options)
+        stdout, stderr = process.communicate()
+        exitcode = process.wait()
+        output = support.strip_python_stderr(stdout)
+        output = output.decode('ascii', 'backslashreplace')
+        if filename:
+            self.assertEqual(output, '')
+            with open(filename, "rb") as fp:
+                output = fp.read()
+            output = output.decode('ascii', 'backslashreplace')
+        output = re.sub('Current thread 0x[0-9a-f]+',
+                        'Current thread XXX',
+                        output)
+        return output.splitlines(), exitcode
+
+    def test_traceback_verbiage(self):
+        code = """
+try:
+    raise ValueError
+except:
+    raise NameError from None
+"""
+        text, exitcode = self.get_output(code)
+        self.assertEqual(len(text), 3)
+        self.assertTrue(text[0].startswith('Traceback'))
+        self.assertTrue(text[1].startswith('  File '))
+        self.assertTrue(text[2].startswith('NameError'))
+
     def test_sets_traceback(self):
         try:
             raise IndexError()
diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py
index 4752d37..5bce2af 100644
--- a/Lib/test/test_traceback.py
+++ b/Lib/test/test_traceback.py
@@ -246,6 +246,21 @@
         self.check_zero_div(blocks[0])
         self.assertIn('inner_raise() # Marker', blocks[2])
 
+    def test_context_suppression(self):
+        try:
+            try:
+                raise Exception
+            except:
+                raise ZeroDivisionError from None
+        except ZeroDivisionError as _:
+            e = _
+        lines = self.get_report(e).splitlines()
+        self.assertEqual(len(lines), 4)
+        self.assertTrue(lines[0].startswith('Traceback'))
+        self.assertTrue(lines[1].startswith('  File'))
+        self.assertIn('ZeroDivisionError from None', lines[2])
+        self.assertTrue(lines[3].startswith('ZeroDivisionError'))
+
     def test_cause_and_context(self):
         # When both a cause and a context are set, only the cause should be
         # displayed and the context should be muted.