Merge 3.5

Issue #21925: warnings.formatwarning() now catches exceptions when calling
linecache.getline() and tracemalloc.get_object_traceback() to be able to log
ResourceWarning emitted late during the Python shutdown process.
diff --git a/Lib/test/test_warnings/__init__.py b/Lib/test/test_warnings/__init__.py
index e6f47cd..9f1cd75 100644
--- a/Lib/test/test_warnings/__init__.py
+++ b/Lib/test/test_warnings/__init__.py
@@ -536,6 +536,14 @@
             self.module._setoption('error::Warning::0')
             self.assertRaises(UserWarning, self.module.warn, 'convert to error')
 
+
+class CWCmdLineTests(WCmdLineTests, unittest.TestCase):
+    module = c_warnings
+
+
+class PyWCmdLineTests(WCmdLineTests, unittest.TestCase):
+    module = py_warnings
+
     def test_improper_option(self):
         # Same as above, but check that the message is printed out when
         # the interpreter is executed. This also checks that options are
@@ -552,12 +560,6 @@
         self.assertFalse(out.strip())
         self.assertNotIn(b'RuntimeWarning', err)
 
-class CWCmdLineTests(WCmdLineTests, unittest.TestCase):
-    module = c_warnings
-
-class PyWCmdLineTests(WCmdLineTests, unittest.TestCase):
-    module = py_warnings
-
 
 class _WarningsTests(BaseTest, unittest.TestCase):
 
@@ -976,6 +978,7 @@
             # Use -W to load warnings module at startup
             assert_python_ok('-c', 'pass', '-W', 'always', PYTHONPATH=cwd)
 
+
 class FinalizationTest(unittest.TestCase):
     def test_finalization(self):
         # Issue #19421: warnings.warn() should not crash
@@ -995,6 +998,23 @@
         # of the script
         self.assertEqual(err, b'__main__:7: UserWarning: test')
 
+    def test_late_resource_warning(self):
+        # Issue #21925: Emitting a ResourceWarning late during the Python
+        # shutdown must be logged.
+
+        expected = b"sys:1: ResourceWarning: unclosed file "
+
+        # don't import the warnings module
+        # (_warnings will try to import it)
+        code = "f = open(%a)" % __file__
+        rc, out, err = assert_python_ok("-c", code)
+        self.assertTrue(err.startswith(expected), ascii(err))
+
+        # import the warnings module
+        code = "import warnings; f = open(%a)" % __file__
+        rc, out, err = assert_python_ok("-c", code)
+        self.assertTrue(err.startswith(expected), ascii(err))
+
 
 def setUpModule():
     py_warnings.onceregistry.clear()
diff --git a/Lib/warnings.py b/Lib/warnings.py
index d4f591e..1ece514 100644
--- a/Lib/warnings.py
+++ b/Lib/warnings.py
@@ -33,26 +33,47 @@
         pass
 
 def _formatwarnmsg_impl(msg):
-    import linecache
     s =  ("%s:%s: %s: %s\n"
           % (msg.filename, msg.lineno, msg.category.__name__,
              msg.message))
+
     if msg.line is None:
-        line = linecache.getline(msg.filename, msg.lineno)
+        try:
+            import linecache
+            line = linecache.getline(msg.filename, msg.lineno)
+        except Exception:
+            # When a warning is logged during Python shutdown, linecache
+            # and the improt machinery don't work anymore
+            line = None
+            linecache = None
     else:
         line = msg.line
     if line:
         line = line.strip()
         s += "  %s\n" % line
+
     if msg.source is not None:
-        import tracemalloc
-        tb = tracemalloc.get_object_traceback(msg.source)
+        try:
+            import tracemalloc
+            tb = tracemalloc.get_object_traceback(msg.source)
+        except Exception:
+            # When a warning is logged during Python shutdown, tracemalloc
+            # and the import machinery don't work anymore
+            tb = None
+
         if tb is not None:
             s += 'Object allocated at (most recent call first):\n'
             for frame in tb:
                 s += ('  File "%s", lineno %s\n'
                       % (frame.filename, frame.lineno))
-                line = linecache.getline(frame.filename, frame.lineno)
+
+                try:
+                    if linecache is not None:
+                        line = linecache.getline(frame.filename, frame.lineno)
+                    else:
+                        line = None
+                except Exception:
+                    line = None
                 if line:
                     line = line.strip()
                     s += '    %s\n' % line