diff --git twisted/python/compat.py twisted/python/compat.py
index 7a56f86..cf4ffdd 100644
--- twisted/python/compat.py
+++ twisted/python/compat.py
@@ -357,6 +357,56 @@ integer.
 """
 
 
+
+if _PY3:
+    def chr_(i):
+        if isinstance(i, int):
+            if i > 255:
+                raise ValueError("chr() arg not in range(256)")
+            return chr(i).encode("latin1")
+        else:
+            raise TypeError("an integer is required")
+else:
+    # We need to emulate 2.x? THIS. IS. 2.X!
+    def chr_(i):
+        return chr(i)
+
+chr_.__doc__ = """
+Duplicate the behavior of 2x chr in 3x.
+
+The Python 2.x built-in chr converted integers in [0, 255] to the
+one-character byte strings containing the character with that ASCII encoded
+value.
+
+The Python 3.x chr instead takes integers in the range [0, 1114111], and
+converts them to one-character Unicode strings containing the character
+with that code-point.
+
+The chr_ in this function duplicates 2.x behavior on both 2.x and 3.x.
+
+See these extracts from the Python manual for details.
+
+Python 2.x
+----------
+chr:
+"Return a string of one character whose ASCII code is the integer i. For
+example, chr(97) returns the string 'a'. This is the inverse of ord(). The
+argument must be in the range [0..255], inclusive; ValueError will be raised if
+i is outside that range. See also unichr()."
+http://docs.python.org/library/functions.html#chr
+
+Python 3.x
+----------
+chr:
+"Return the string representing a character whose Unicode codepoint is the
+integer i. For example, chr(97) returns the string 'a'. This is the inverse of
+ord(). The valid range for the argument is from 0 through 1,114,111 (0x10FFFF
+in base 16). ValueError will be raised if i is outside that range."
+http://docs.python.org/py3k/library/functions.html#chr
+"""
+
+
+
 __all__ = [
     "reraise",
     "execfile",
@@ -370,4 +420,5 @@ __all__ = [
     "unicode",
     "iterbytes",
     "intToBytes",
+    "chr_"
     ]
diff --git twisted/test/test_compat.py twisted/test/test_compat.py
index b408153..b41c4ae 100644
--- twisted/test/test_compat.py
+++ twisted/test/test_compat.py
@@ -16,6 +16,7 @@ from twisted.python.compat import set, frozenset, reduce, execfile, _PY3
 from twisted.python.compat import comparable, cmp, nativeString
 from twisted.python.compat import unicode as unicodeCompat
 from twisted.python.compat import reraise, NativeStringIO, iterbytes, intToBytes
+from twisted.python.compat import chr_
 from twisted.python.filepath import FilePath
 
 
@@ -538,3 +539,91 @@ class Python3BytesTests(unittest.SynchronousTestCase):
         ASCII-encoded string representation of the number.
         """
         self.assertEqual(intToBytes(213), b"213")
+
+
+
+class chr_Tests(unittest.SynchronousTestCase):
+    """
+    Tests for L{compat.chr_}.
+
+    L{compat.chr_} should emulate the Python 2.x built-in chr which had
+    these key characteristics:
+        -Accepted an integer in [0, 255].
+        -Returned a bytestring containing the character encoded to that
+         integer under ASCII.
+        -Raised a ValueError in response to any integer outside that range.
+        -Raised a TypeError in response to any other input.
+    """
+    # There are only 256 ASCII-representable characters, so let's just test
+    # them all.
+    _allASCIICharacters = [b"\x00", b"\x01", b"\x02", b"\x03", b"\x04", b"\x05", b"\x06", b"\x07", b"\x08", b"\t", b"\n", b"\x0b", b"\x0c", b"\r", b"\x0e", b"\x0f", b"\x10", b"\x11", b"\x12", b"\x13", b"\x14", b"\x15", b"\x16", b"\x17", b"\x18", b"\x19", b"\x1a", b"\x1b", b"\x1c", b"\x1d", b"\x1e", b"\x1f", b" ", b"!", b"\"", b"#", b"$", b"%", b"&", b"\'", b"(", b")", b"*", b"+", b",", b"-", b".", b"/", b"0", b"1", b"2", b"3", b"4", b"5", b"6", b"7", b"8", b"9", b":", b";", b"<", b"=", b">", b"?", b"@", b"A", b"B", b"C", b"D", b"E", b"F", b"G", b"H", b"I", b"J", b"K", b"L", b"M", b"N", b"O", b"P", b"Q", b"R", b"S", b"T", b"U", b"V", b"W", b"X", b"Y", b"Z", b"[", b"\\", b"]", b"^", b"_", b"`", b"a", b"b", b"c", b"d", b"e", b"f", b"g", b"h", b"i", b"j", b"k", b"l", b"m", b"n", b"o", b"p", b"q", b"r", b"s", b"t", b"u", b"v", b"w", b"x", b"y", b"z", b"{", b"|", b"}", b"~", b"\x7f", b"\x80", b"\x81", b"\x82", b"\x83", b"\x84", b"\x85", b"\x86", b"\x87", b"\x88", b"\x89", b"\x8a", b"\x8b", b"\x8c", b"\x8d", b"\x8e", b"\x8f", b"\x90", b"\x91", b"\x92", b"\x93", b"\x94", b"\x95", b"\x96", b"\x97", b"\x98", b"\x99", b"\x9a", b"\x9b", b"\x9c", b"\x9d", b"\x9e", b"\x9f", b"\xa0", b"\xa1", b"\xa2", b"\xa3", b"\xa4", b"\xa5", b"\xa6", b"\xa7", b"\xa8", b"\xa9", b"\xaa", b"\xab", b"\xac", b"\xad", b"\xae", b"\xaf", b"\xb0", b"\xb1", b"\xb2", b"\xb3", b"\xb4", b"\xb5", b"\xb6", b"\xb7", b"\xb8", b"\xb9", b"\xba", b"\xbb", b"\xbc", b"\xbd", b"\xbe", b"\xbf", b"\xc0", b"\xc1", b"\xc2", b"\xc3", b"\xc4", b"\xc5", b"\xc6", b"\xc7", b"\xc8", b"\xc9", b"\xca", b"\xcb", b"\xcc", b"\xcd", b"\xce", b"\xcf", b"\xd0", b"\xd1", b"\xd2", b"\xd3", b"\xd4", b"\xd5", b"\xd6", b"\xd7", b"\xd8", b"\xd9", b"\xda", b"\xdb", b"\xdc", b"\xdd", b"\xde", b"\xdf", b"\xe0", b"\xe1", b"\xe2", b"\xe3", b"\xe4", b"\xe5", b"\xe6", b"\xe7", b"\xe8", b"\xe9", b"\xea", b"\xeb", b"\xec", b"\xed", b"\xee", b"\xef", b"\xf0", b"\xf1", b"\xf2", b"\xf3", b"\xf4", b"\xf5", b"\xf6", b"\xf7", b"\xf8", b"\xf9", b"\xfa", b"\xfb", b"\xfc", b"\xfd", b"\xfe", b"\xff"]
+    _ASCIIEncodedValues = list(range(256))
+    # And here is a sampling of things that chr_ should NOT accept.
+    _negativeIntegers = [-10, -1]
+    _integersOver255 = [256, 257, 500, 1000, 10000, 60000, 65534, 65535]
+    _integersOver65535 = [65536, 65537, 100000]
+    _bytestringASCIICharacters = [b"a", b"\x15"]
+    _UnicodeCharacters = [u"b", u"\xeff", u"\xfffe", u"\xffff"]
+    _UnicodeCharactersOver65535 = [u"\x10000", u"\x10001"]
+    _nonIntegersNotCharacters = [2.3, dict()]
+
+    def test_chr_Accepts(self):
+        """
+        L{compat.chr_} accepts integers in [0, 255] and returns the
+        corresponding character in an ASCII-encoded bytestring.
+        """
+        validIntegers = self._ASCIIEncodedValues
+        for i in validIntegers:
+            # Deliberately leak any exceptions; failing the test with ERROR.
+            ret = chr_(i)
+            self.assertIsInstance(ret, bytes)
+            self.assertEqual(ret, self._allASCIICharacters[i])
+
+    def _assert_chr_Rejects(self, invalidInput, exceptionToRejectWith):
+        """Convenience function. map assertRaises across invalidInput."""
+        for x in invalidInput:
+            self.assertRaises(exceptionToRejectWith, chr_, x)
+
+    def test_chr_RejectsNegativeIntegers(self):
+        """
+        L{compat.chr_} rejects negative integers with ValueError.
+        """
+        self._assert_chr_Rejects(self._negativeIntegers, ValueError)
+
+    def test_chr_RejectsIntegersOver255(self):
+        """
+        L{compat.chr_} rejects integers over 255 with ValueError.
+        """
+        self._assert_chr_Rejects(self._integersOver255, ValueError)
+
+    def test_chr_RejectsIntegersOver65535(self):
+        """
+        L{compat.chr_} rejects integers over 65535 with ValueError.
+        """
+        self._assert_chr_Rejects(self._integersOver65535, ValueError)
+
+    def test_chr_RejectsBytestringASCIICharacters(self):
+        """
+        L{compat.chr_} rejects ASCII-encoded bytestrings with TypeError.
+        """
+        self._assert_chr_Rejects(self._bytestringASCIICharacters, TypeError)
+
+    def test_chr_RejectsUnicodeCharacters(self):
+        """
+        L{compat.chr_} rejects Unicode strings with TypeError.
+        """
+        self._assert_chr_Rejects(self._UnicodeCharacters, TypeError)
+
+    def test_chr_RejectsUnicodeCharactersOver65535(self):
+        """
+        L{compat.chr_} rejects Unicode strings with codepoints over 65535 with
+        TypeError.
+        """
+        self._assert_chr_Rejects(self._UnicodeCharactersOver65535, TypeError)
+
+    def test_chr_RejectsNonIntegersNonCharacters(self):
+        """
+        L{compat.chr_} rejects any other odd thing you give it, like a dict,
+        with TypeError.
+        """
+        self._assert_chr_Rejects(self._nonIntegersNotCharacters, TypeError)
diff --git twisted/topfiles/6070.misc twisted/topfiles/6070.misc
new file mode 100644
index 0000000..e69de29
