Patch #912410: Replace HTML entity references for attribute values
in HTMLParser.
diff --git a/Lib/HTMLParser.py b/Lib/HTMLParser.py
index 8380466..2cbc2ec 100644
--- a/Lib/HTMLParser.py
+++ b/Lib/HTMLParser.py
@@ -358,12 +358,30 @@
         self.error("unknown declaration: %r" % (data,))
 
     # Internal -- helper to remove special character quoting
+    entitydefs = None
     def unescape(self, s):
         if '&' not in s:
             return s
-        s = s.replace("&lt;", "<")
-        s = s.replace("&gt;", ">")
-        s = s.replace("&apos;", "'")
-        s = s.replace("&quot;", '"')
-        s = s.replace("&amp;", "&") # Must be last
-        return s
+        def replaceEntities(s):
+            s = s.groups()[0]
+            if s[0] == "#":
+                s = s[1:]
+                if s[0] in ['x','X']:
+                    c = int(s[1:], 16)
+                else:
+                    c = int(s)
+                return unichr(c)
+            else:
+                # Cannot use name2codepoint directly, because HTMLParser supports apos,
+                # which is not part of HTML 4
+                import htmlentitydefs
+                if HTMLParser.entitydefs is None:
+                    entitydefs = HTMLParser.entitydefs = {'apos':u"'"}
+                    for k, v in htmlentitydefs.name2codepoint.iteritems():
+                        entitydefs[k] = unichr(v)
+                try:
+                    return self.entitydefs[s]
+                except KeyError:
+                    return '&'+s+';'
+
+        return re.sub(r"&(#?[xX]?(?:[0-9a-fA-F]+|\w{1,8}));", replaceEntities, s)
diff --git a/Lib/test/test_htmlparser.py b/Lib/test/test_htmlparser.py
index 54b90cd..229bbed 100755
--- a/Lib/test/test_htmlparser.py
+++ b/Lib/test/test_htmlparser.py
@@ -309,6 +309,11 @@
             ("endtag", "script"),
             ])
 
+    def test_entityrefs_in_attributes(self):
+        self._run_check("<html foo='&euro;&amp;&#97;&#x61;&unsupported;'>", [
+                ("starttag", "html", [("foo", u"\u20AC&aa&unsupported;")])
+                ])
+
 
 def test_main():
     test_support.run_unittest(HTMLParserTestCase)