Skip to content

Commit 821dffc

Browse files
gh-63866: Add the "htmlcharrefreplace" error handler
Add html.htmlcharrefreplace_errors() which replaces an unencodable character with the corresponding HTML named character reference, or with a numeric character reference if there is no name for it. It is not registered as an error handler by default. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent 3874ad1 commit 821dffc

4 files changed

Lines changed: 64 additions & 2 deletions

File tree

Doc/library/html.rst

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,25 @@ This module defines utilities to manipulate HTML.
3434

3535
.. versionadded:: 3.4
3636

37+
38+
.. function:: htmlcharrefreplace_errors(exception)
39+
40+
Implements the ``htmlcharrefreplace`` error handling (for encoding only):
41+
the unencodable character is replaced by the corresponding HTML named
42+
character reference from :data:`html.entities.codepoint2name`, or by a
43+
numeric character reference if there is no name for it.
44+
45+
This error handler is not registered by default, you should register it
46+
with :func:`codecs.register_error`::
47+
48+
>>> import codecs, html
49+
>>> codecs.register_error('htmlcharrefreplace',
50+
... html.htmlcharrefreplace_errors)
51+
>>> '∀ x∈ℜ'.encode('ascii', 'htmlcharrefreplace')
52+
b'&forall; x&isin;&real;'
53+
54+
.. versionadded:: next
55+
3756
--------------
3857

3958
Submodules in the ``html`` package are:

Lib/html/__init__.py

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@
33
"""
44

55
import re as _re
6-
from html.entities import html5 as _html5
6+
from html.entities import codepoint2name as _codepoint2name, html5 as _html5
77

88

9-
__all__ = ['escape', 'unescape']
9+
__all__ = ['escape', 'unescape', 'htmlcharrefreplace_errors']
1010

1111

1212
def escape(s, quote=True):
@@ -130,3 +130,20 @@ def unescape(s):
130130
if '&' not in s:
131131
return s
132132
return _charref.sub(_replace_charref, s)
133+
134+
135+
def htmlcharrefreplace_errors(exception):
136+
"""Implements the 'htmlcharrefreplace' error handling.
137+
138+
Replaces an unencodable character with the corresponding HTML named
139+
character reference, or with a numeric character reference if there
140+
is no name for it.
141+
"""
142+
if not isinstance(exception, UnicodeEncodeError):
143+
raise exception
144+
replace = []
145+
for c in exception.object[exception.start:exception.end]:
146+
n = ord(c)
147+
name = _codepoint2name.get(n)
148+
replace.append(f'&{name};' if name is not None else f'&#{n};')
149+
return ''.join(replace), exception.end

Lib/test/test_html.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
Tests for the html module functions.
33
"""
44

5+
import codecs
56
import html
67
import unittest
78

@@ -98,6 +99,27 @@ def check_num(num, expected):
9899
'ÉricÉric&alphacentauriαcentauri')
99100
check('&co;', '&co;')
100101

102+
def test_htmlcharrefreplace_errors(self):
103+
codecs.register_error('htmlcharrefreplace',
104+
html.htmlcharrefreplace_errors)
105+
self.assertEqual('\u2200 x\u2208\u211c'.encode('ascii',
106+
'htmlcharrefreplace'),
107+
b'&forall; x&isin;&real;')
108+
# Characters without a name are replaced with a numeric reference.
109+
self.assertEqual('[$\xa5\u20a3\u20ac\U0001d56b]'.encode(
110+
'latin1', 'htmlcharrefreplace'),
111+
b'[$\xa5&#8355;&euro;&#120171;]')
112+
# Surrogates have no name either.
113+
self.assertEqual('\udcff'.encode('ascii', 'htmlcharrefreplace'),
114+
b'&#56575;')
115+
116+
def test_htmlcharrefreplace_errors_bad_exception(self):
117+
with self.assertRaises(UnicodeDecodeError):
118+
html.htmlcharrefreplace_errors(
119+
UnicodeDecodeError('ascii', b'\xff', 0, 1, 'ordinal'))
120+
with self.assertRaises(TypeError):
121+
html.htmlcharrefreplace_errors(TypeError('spam'))
122+
101123

102124
if __name__ == '__main__':
103125
unittest.main()
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Add :func:`html.htmlcharrefreplace_errors` which implements the
2+
``htmlcharrefreplace`` error handler: an unencodable character is replaced
3+
with the corresponding HTML named or numeric character reference. It is not
4+
registered by default.

0 commit comments

Comments
 (0)