Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions Doc/library/html.rst
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,25 @@ This module defines utilities to manipulate HTML.

.. versionadded:: 3.4


.. function:: htmlcharrefreplace_errors(exception)

Implements the ``htmlcharrefreplace`` error handling (for encoding only):
the unencodable character is replaced by the corresponding HTML named
character reference from :data:`html.entities.codepoint2name`, or by a
numeric character reference if there is no name for it.

This error handler is not registered by default, you should register it
with :func:`codecs.register_error`::

>>> import codecs, html
>>> codecs.register_error('htmlcharrefreplace',
... html.htmlcharrefreplace_errors)
>>> '∀ x∈ℜ'.encode('ascii', 'htmlcharrefreplace')
b'∀ x∈ℜ'

.. versionadded:: next

--------------

Submodules in the ``html`` package are:
Expand Down
21 changes: 19 additions & 2 deletions Lib/html/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
"""

import re as _re
from html.entities import html5 as _html5
from html.entities import codepoint2name as _codepoint2name, html5 as _html5


__all__ = ['escape', 'unescape']
__all__ = ['escape', 'unescape', 'htmlcharrefreplace_errors']


def escape(s, quote=True):
Expand Down Expand Up @@ -130,3 +130,20 @@ def unescape(s):
if '&' not in s:
return s
return _charref.sub(_replace_charref, s)


def htmlcharrefreplace_errors(exception):
"""Implements the 'htmlcharrefreplace' error handling.

Replaces an unencodable character with the corresponding HTML named
character reference, or with a numeric character reference if there
is no name for it.
"""
if not isinstance(exception, UnicodeEncodeError):
raise exception
replace = []
for c in exception.object[exception.start:exception.end]:
n = ord(c)
name = _codepoint2name.get(n)
replace.append(f'&{name};' if name is not None else f'&#{n};')
return ''.join(replace), exception.end
22 changes: 22 additions & 0 deletions Lib/test/test_html.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
Tests for the html module functions.
"""

import codecs
import html
import unittest

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

def test_htmlcharrefreplace_errors(self):
codecs.register_error('htmlcharrefreplace',
html.htmlcharrefreplace_errors)
self.assertEqual('\u2200 x\u2208\u211c'.encode('ascii',
'htmlcharrefreplace'),
b'∀ x∈ℜ')
# Characters without a name are replaced with a numeric reference.
self.assertEqual('[$\xa5\u20a3\u20ac\U0001d56b]'.encode(
'latin1', 'htmlcharrefreplace'),
b'[$\xa5₣€𝕫]')
# Surrogates have no name either.
self.assertEqual('\udcff'.encode('ascii', 'htmlcharrefreplace'),
b'�')

def test_htmlcharrefreplace_errors_bad_exception(self):
with self.assertRaises(UnicodeDecodeError):
html.htmlcharrefreplace_errors(
UnicodeDecodeError('ascii', b'\xff', 0, 1, 'ordinal'))
with self.assertRaises(TypeError):
html.htmlcharrefreplace_errors(TypeError('spam'))


if __name__ == '__main__':
unittest.main()
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Add :func:`html.htmlcharrefreplace_errors` which implements the
``htmlcharrefreplace`` error handler: an unencodable character is replaced
with the corresponding HTML named or numeric character reference. It is not
registered by default.
Loading