diff -r f2d1dba10a0e Lib/test/test_sax.py --- a/Lib/test/test_sax.py Thu Jun 30 14:03:21 2016 +0300 +++ b/Lib/test/test_sax.py Sun Jul 03 20:15:42 2016 +0800 @@ -261,6 +261,11 @@ self.assertEqual(escape("Hei på deg", {"å" : "å"}), "Hei på deg") + def test_escape_not_override(self): + self.assertEqual(escape("Donald Duck & Co.", + {";": ".", ".": ";"}), + "Donald Duck & Co;") + # ===== unescape def test_unescape_basic(self): self.assertEqual(unescape("Donald Duck & Co"), "Donald Duck & Co") @@ -276,6 +281,11 @@ def test_unescape_amp_extra(self): self.assertEqual(unescape("&foo;", {"&foo;": "splat"}), "&foo;") + def test_unescape_not_override(self): + self.assertEqual(unescape("<Donald Duck & Co>", + {"<": "[", ">": "]"}), + "") + # ===== quoteattr def test_quoteattr_basic(self): self.assertEqual(quoteattr("Donald Duck & Co"), diff -r f2d1dba10a0e Lib/xml/sax/saxutils.py --- a/Lib/xml/sax/saxutils.py Thu Jun 30 14:03:21 2016 +0300 +++ b/Lib/xml/sax/saxutils.py Sun Jul 03 20:15:42 2016 +0800 @@ -5,16 +5,11 @@ import os, urllib.parse, urllib.request import io +import re import codecs from . import handler from . import xmlreader -def __dict_replace(s, d): - """Replace substrings of a string using a dictionary.""" - for key, value in d.items(): - s = s.replace(key, value) - return s - def escape(data, entities={}): """Escape &, <, and > in a string of data. @@ -22,14 +17,10 @@ the optional entities parameter. The keys and values must all be strings; each key will be replaced with its corresponding value. """ - - # must do ampersand first - data = data.replace("&", "&") - data = data.replace(">", ">") - data = data.replace("<", "<") - if entities: - data = __dict_replace(data, entities) - return data + entities = entities.copy() + entities.update({"&": "&", ">": ">", "<": "<"}) + pattern = "|".join(re.escape(c) for c in entities) + return re.sub(pattern, lambda m: entities[m.group(0)], data) def unescape(data, entities={}): """Unescape &, <, and > in a string of data. @@ -38,12 +29,10 @@ the optional entities parameter. The keys and values must all be strings; each key will be replaced with its corresponding value. """ - data = data.replace("<", "<") - data = data.replace(">", ">") - if entities: - data = __dict_replace(data, entities) - # must do ampersand last - return data.replace("&", "&") + entities = entities.copy() + entities.update({"<": "<", ">": ">", "&": "&"}) + pattern = "|".join(re.escape(v) for v in entities) + return re.sub(pattern, lambda m: entities[m.group(0)], data) def quoteattr(data, entities={}): """Escape and quote an attribute value.