Issue #5027: The standard `xml` namespace is now understood by
xml.sax.saxutils.XMLGenerator as being bound to
http://www.w3.org/XML/1998/namespace.  Patch by Troy J. Farrell.
diff --git a/Lib/test/test_sax.py b/Lib/test/test_sax.py
index 143ddf2..c4b211f 100644
--- a/Lib/test/test_sax.py
+++ b/Lib/test/test_sax.py
@@ -11,6 +11,7 @@
 from xml.sax.saxutils import XMLGenerator, escape, unescape, quoteattr, \
                              XMLFilterBase
 from xml.sax.expatreader import create_parser
+from xml.sax.handler import feature_namespaces
 from xml.sax.xmlreader import InputSource, AttributesImpl, AttributesNSImpl
 from io import StringIO
 from test.support import findfile, run_unittest
@@ -385,6 +386,60 @@
         self.assertEquals(result.getvalue(),
             start+'<my:a xmlns:my="qux" b="c"/>')
 
+    def test_5027_1(self):
+        # The xml prefix (as in xml:lang below) is reserved and bound by
+        # definition to http://www.w3.org/XML/1998/namespace.  XMLGenerator had
+        # a bug whereby a KeyError is thrown because this namespace is missing
+        # from a dictionary.
+        #
+        # This test demonstrates the bug by parsing a document.
+        test_xml = StringIO(
+            '<?xml version="1.0"?>'
+            '<a:g1 xmlns:a="http://example.com/ns">'
+             '<a:g2 xml:lang="en">Hello</a:g2>'
+            '</a:g1>')
+
+        parser = make_parser()
+        parser.setFeature(feature_namespaces, True)
+        result = StringIO()
+        gen = XMLGenerator(result)
+        parser.setContentHandler(gen)
+        parser.parse(test_xml)
+
+        self.assertEquals(result.getvalue(),
+                          start + (
+                          '<a:g1 xmlns:a="http://example.com/ns">'
+                           '<a:g2 xml:lang="en">Hello</a:g2>'
+                          '</a:g1>'))
+
+    def test_5027_2(self):
+        # The xml prefix (as in xml:lang below) is reserved and bound by
+        # definition to http://www.w3.org/XML/1998/namespace.  XMLGenerator had
+        # a bug whereby a KeyError is thrown because this namespace is missing
+        # from a dictionary.
+        #
+        # This test demonstrates the bug by direct manipulation of the
+        # XMLGenerator.
+        result = StringIO()
+        gen = XMLGenerator(result)
+
+        gen.startDocument()
+        gen.startPrefixMapping('a', 'http://example.com/ns')
+        gen.startElementNS(('http://example.com/ns', 'g1'), 'g1', {})
+        lang_attr = {('http://www.w3.org/XML/1998/namespace', 'lang'): 'en'}
+        gen.startElementNS(('http://example.com/ns', 'g2'), 'g2', lang_attr)
+        gen.characters('Hello')
+        gen.endElementNS(('http://example.com/ns', 'g2'), 'g2')
+        gen.endElementNS(('http://example.com/ns', 'g1'), 'g1')
+        gen.endPrefixMapping('a')
+        gen.endDocument()
+
+        self.assertEquals(result.getvalue(),
+                          start + (
+                          '<a:g1 xmlns:a="http://example.com/ns">'
+                           '<a:g2 xml:lang="en">Hello</a:g2>'
+                          '</a:g1>'))
+
 
 class XMLFilterBaseTest(unittest.TestCase):
     def test_filter_basic(self):
diff --git a/Lib/xml/sax/saxutils.py b/Lib/xml/sax/saxutils.py
index 46946fc..625bc12 100644
--- a/Lib/xml/sax/saxutils.py
+++ b/Lib/xml/sax/saxutils.py
@@ -100,6 +100,12 @@
     def _qname(self, name):
         """Builds a qualified name from a (ns_url, localname) pair"""
         if name[0]:
+            # Per http://www.w3.org/XML/1998/namespace, The 'xml' prefix is
+            # bound by definition to http://www.w3.org/XML/1998/namespace.  It
+            # does not need to be declared and will not usually be found in
+            # self._current_context.
+            if 'http://www.w3.org/XML/1998/namespace' == name[0]:
+                return 'xml:' + name[1]
             # The name is in a non-empty namespace
             prefix = self._current_context[name[0]]
             if prefix: