Issue #19949: The test_xpickle test now tests compatibility with installed
Python 2.7 and reports skipped tests.  Based on patch by Zachary Ware.
diff --git a/Lib/test/test_xpickle.py b/Lib/test/test_xpickle.py
index ac3a33f..b6973f6 100644
--- a/Lib/test/test_xpickle.py
+++ b/Lib/test/test_xpickle.py
@@ -56,7 +56,7 @@
         # Ignore fast
         return cPickle.loads(buf)
 
-def have_python_version(name):
+def have_python_version(name, cache={}):
     """Check whether the given name is a valid Python binary and has
     test.test_support.
 
@@ -68,7 +68,9 @@
     Returns:
         True if the name is valid, False otherwise.
     """
-    return os.system(name + " -c 'import test.test_support'") == 0
+    if name not in cache:
+        cache[name] = os.system(name + ' -c "import test.test_support"') == 0
+    return cache[name]
 
 
 class AbstractCompatTests(AbstractPickleTests):
@@ -81,6 +83,9 @@
         self.assertTrue(self.python)
         self.assertTrue(self.module)
         self.assertTrue(self.error)
+        test_support.requires("xpickle")
+        if not have_python_version(self.python):
+            self.skipTest('%s not available' % self.python)
 
     def send_to_worker(self, python, obj, proto):
         """Bounce a pickled object through another version of Python.
@@ -119,14 +124,9 @@
 
     # These tests are disabled because they require some special setup
     # on the worker that's hard to keep in sync.
-    def test_global_ext1(self):
-        pass
-
-    def test_global_ext2(self):
-        pass
-
-    def test_global_ext4(self):
-        pass
+    test_global_ext1 = None
+    test_global_ext2 = None
+    test_global_ext4 = None
 
     # This is a cut-down version of pickletester's test_float. Backwards
     # compatibility for the values in for_bin_protos was explicitly broken in
@@ -151,48 +151,34 @@
                 self.assertEqual(value, got)
 
     # Backwards compatibility was explicitly broken in r67934 to fix a bug.
-    def test_unicode_high_plane(self):
-        pass
+    test_unicode_high_plane = None
 
     # This tests a fix that's in 2.7 only
-    def test_dynamic_class(self):
-        pass
+    test_dynamic_class = None
 
-    if test_support.have_unicode:
-        # This is a cut-down version of pickletester's test_unicode. Backwards
-        # compatibility was explicitly broken in r67934 to fix a bug.
-        def test_unicode(self):
-            endcases = [u'', u'<\\u>', u'<\\\u1234>', u'<\n>', u'<\\>']
-            for proto in pickletester.protocols:
-                for u in endcases:
-                    p = self.dumps(u, proto)
-                    u2 = self.loads(p)
-                    self.assertEqual(u2, u)
-
-
-def run_compat_test(python_name):
-    return (test_support.is_resource_enabled("xpickle") and
-            have_python_version(python_name))
+    # This is a cut-down version of pickletester's test_unicode. Backwards
+    # compatibility was explicitly broken in r67934 to fix a bug.
+    @unittest.skipUnless(test_support.have_unicode, 'no unicode support')
+    def test_unicode(self):
+        endcases = [u'', u'<\\u>', u'<\\%c>' % 0x1234, u'<\n>', u'<\\>']
+        for proto in pickletester.protocols:
+            for u in endcases:
+                p = self.dumps(u, proto)
+                u2 = self.loads(p)
+                self.assertEqual(u2, u)
 
 
 # Test backwards compatibility with Python 2.4.
-if not run_compat_test("python2.4"):
-    class CPicklePython24Compat(unittest.TestCase):
-        pass
-else:
-    class CPicklePython24Compat(AbstractCompatTests):
+class CPicklePython24Compat(AbstractCompatTests):
 
-        module = cPickle
-        python = "python2.4"
-        error = cPickle.BadPickleGet
+    module = cPickle
+    python = "python2.4"
+    error = cPickle.BadPickleGet
 
-        # Disable these tests for Python 2.4. Making them pass would require
-        # nontrivially monkeypatching the pickletester module in the worker.
-        def test_reduce_calls_base(self):
-            pass
-
-        def test_reduce_ex_calls_base(self):
-            pass
+    # Disable these tests for Python 2.4. Making them pass would require
+    # nontrivially monkeypatching the pickletester module in the worker.
+    test_reduce_calls_base = None
+    test_reduce_ex_calls_base = None
 
 class PicklePython24Compat(CPicklePython24Compat):
 
@@ -201,15 +187,11 @@
 
 
 # Test backwards compatibility with Python 2.5.
-if not run_compat_test("python2.5"):
-    class CPicklePython25Compat(unittest.TestCase):
-        pass
-else:
-    class CPicklePython25Compat(AbstractCompatTests):
+class CPicklePython25Compat(AbstractCompatTests):
 
-        module = cPickle
-        python = "python2.5"
-        error = cPickle.BadPickleGet
+    module = cPickle
+    python = "python2.5"
+    error = cPickle.BadPickleGet
 
 class PicklePython25Compat(CPicklePython25Compat):
 
@@ -218,15 +200,11 @@
 
 
 # Test backwards compatibility with Python 2.6.
-if not run_compat_test("python2.6"):
-    class CPicklePython26Compat(unittest.TestCase):
-        pass
-else:
-    class CPicklePython26Compat(AbstractCompatTests):
+class CPicklePython26Compat(AbstractCompatTests):
 
-        module = cPickle
-        python = "python2.6"
-        error = cPickle.BadPickleGet
+    module = cPickle
+    python = "python2.6"
+    error = cPickle.BadPickleGet
 
 class PicklePython26Compat(CPicklePython26Compat):
 
@@ -234,6 +212,18 @@
     error = KeyError
 
 
+class CPicklePython27Compat(AbstractCompatTests):
+
+    module = cPickle
+    python = "python2.7"
+    error = cPickle.BadPickleGet
+
+class PicklePython27Compat(CPicklePython26Compat):
+
+    module = pickle
+    error = KeyError
+
+
 def worker_main(in_stream, out_stream):
     message = cPickle.load(in_stream)
     protocol, obj = message
@@ -241,20 +231,17 @@
 
 
 def test_main():
-    if not test_support.is_resource_enabled("xpickle"):
-        print >>sys.stderr, "test_xpickle -- skipping backwards compat tests."
-        print >>sys.stderr, "Use 'regrtest.py -u xpickle' to run them."
-        sys.stderr.flush()
-
     test_support.run_unittest(
         DumpCPickle_LoadPickle,
         DumpPickle_LoadCPickle,
         CPicklePython24Compat,
         CPicklePython25Compat,
         CPicklePython26Compat,
+        CPicklePython27Compat,
         PicklePython24Compat,
         PicklePython25Compat,
         PicklePython26Compat,
+        PicklePython27Compat,
     )
 
 if __name__ == "__main__":