bpo-29800: Fix crashes in partial.__repr__ if the keys of partial.keywords are not strings (#649)

diff --git a/Lib/test/test_functools.py b/Lib/test/test_functools.py
index 612ca17..29ea493 100644
--- a/Lib/test/test_functools.py
+++ b/Lib/test/test_functools.py
@@ -403,6 +403,32 @@
         else:
             self.fail('partial object allowed __dict__ to be deleted')
 
+    def test_manually_adding_non_string_keyword(self):
+        p = self.partial(capture)
+        # Adding a non-string/unicode keyword to partial kwargs
+        p.keywords[1234] = 'value'
+        r = repr(p)
+        self.assertIn('1234', r)
+        self.assertIn("'value'", r)
+        with self.assertRaises(TypeError):
+            p()
+
+    def test_keystr_replaces_value(self):
+        p = self.partial(capture)
+
+        class MutatesYourDict(object):
+            def __str__(self):
+                p.keywords[self] = ['sth2']
+                return 'astr'
+
+        # Raplacing the value during key formatting should keep the original
+        # value alive (at least long enough).
+        p.keywords[MutatesYourDict()] = ['sth']
+        r = repr(p)
+        self.assertIn('astr', r)
+        self.assertIn("['sth']", r)
+
+
 class TestPartialPy(TestPartial, unittest.TestCase):
     partial = py_functools.partial
 
diff --git a/Misc/ACKS b/Misc/ACKS
index b7f1282..c99aeaa 100644
--- a/Misc/ACKS
+++ b/Misc/ACKS
Binary files differ
diff --git a/Misc/NEWS b/Misc/NEWS
index b2a1a6e..2f7bd7a 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -281,6 +281,9 @@
 Library
 -------
 
+- bpo-29800: Fix crashes in partial.__repr__ if the keys of partial.keywords
+  are not strings.  Patch by Michael Seifert.
+
 - bpo-8256: Fixed possible failing or crashing input() if attributes "encoding"
   or "errors" of sys.stdin or sys.stdout are not set or are not strings.
 
diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c
index f2af4ab..592edbb 100644
--- a/Modules/_functoolsmodule.c
+++ b/Modules/_functoolsmodule.c
@@ -297,8 +297,11 @@
     /* Pack keyword arguments */
     assert (PyDict_Check(pto->kw));
     for (i = 0; PyDict_Next(pto->kw, &i, &key, &value);) {
-        Py_SETREF(arglist, PyUnicode_FromFormat("%U, %U=%R", arglist,
+        /* Prevent key.__str__ from deleting the value. */
+        Py_INCREF(value);
+        Py_SETREF(arglist, PyUnicode_FromFormat("%U, %S=%R", arglist,
                                                 key, value));
+        Py_DECREF(value);
         if (arglist == NULL)
             goto done;
     }