classification
Title: Automatically convert character references in HTMLParser
Type: enhancement Stage: committed/rejected
Components: Library (Lib) Versions: Python 3.4
process
Status: closed Resolution: fixed
Dependencies: 2927 11113 Superseder:
Assigned To: ezio.melotti Nosy List: eric.araujo, ezio.melotti, python-dev, r.david.murray, serhiy.storchaka
Priority: normal Keywords: patch

Created on 2011-12-19 06:55 by ezio.melotti, last changed 2013-11-23 18:17 by ezio.melotti. This issue is now closed.

Files
File name Uploaded Description Edit
issue13633.diff ezio.melotti, 2013-11-20 18:46 review
issue13633-2.diff ezio.melotti, 2013-11-23 15:44 review
Messages (8)
msg149822 - (view) Author: Ezio Melotti (ezio.melotti) * (Python committer) Date: 2011-12-19 06:55
The doc for handle_charref and handle_entityref say:
"""
HTMLParser.handle_charref(name)
    This method is called to process a character reference of the form "&#ref;". It is intended to be overridden by a derived class; the base class implementation does nothing.

HTMLParser.handle_entityref(name)
    This method is called to process a general entity reference of the form "&name;" where name is an general entity reference. It is intended to be overridden by a derived class; the base class implementation does nothing.
"""

The doc doesn't mention hex references, like ">", and apparently they are passed to handle_charref without the '&#' but with the leading 'x':

>>> from HTMLParser import HTMLParser
>>> class MyParser(HTMLParser):
...   def handle_charref(self, data):
...     print data
... 
>>> MyParser().feed('> > >')
62
x3E

I've seen code in the wild doing unichr(int(data)) in handle_charref (once they figured out that '62' is passed) and then fail when an hex entity is found.  Passing 'x3E' doesn't seem too useful because the user has to first check if there's a leading 'x', if there is remove it, then convert the hex string to int, and finally use unichr() to get the char, otherwise just convert to int and use unichr().

There 3 different possible solutions:
1) just document the behavior;
2) normalize the hex value before passing them to handle_charref and document it;
3) add a new handle_entity method that is called with the character represented by the entity (named, decimal, or hex);

The first solution alone doesn't solve much, but the doc should be clearer regardless of the decision we take.
The second one is better, but if it's implemented there won't be any way to know if the entity had a decimal or hex value anymore (does anyone care?).  The normalization should also convert the hex string to int and then convert it back to str to be consistent with decimal entities.
The third one might be better, but doesn't solve the issue on 2.7/3.2.  People don't care about entities and just want the equivalent char, so having a method that converts them already sounds like a useful feature to me.
msg154036 - (view) Author: Ezio Melotti (ezio.melotti) * (Python committer) Date: 2012-02-23 02:38
This behavior is now documented, but the situation could still be improved.  Adding a new method that receives the converted entity seems a good way to handle this.  The parser can call both, and users can pick either one.

One problem with the current methods (handle_charref and handle_entityref) is that they don't do any processing on the entity and let invalid character references like � or &#iamnotanentity; go through.

There are at least 3 changes that should be done in order to follow the HTML5 standard [0]:
 1) the parser should look at html.entities while parsing named character references (see also #11113).  This will allow the parser to parse &notit; as "¬it;" and ∉ as "∉" (see note at the very end of [0]);
 2) invalid character references (e.g. �, &#iamnotanentity;) should not go through;
 3) the table at [0] with the replacement character should be used by the parser to "correct" those invalid character references (e.g. 0x80 -> U+20AC);

Now, 1) can be done for both the old and new method, but for 2) and 3) the situation is a bit more complicated.  The best thing is probably to keep sending them unchanged to the old methods, and implement the correct behavior for the new method only.

[0]: http://www.w3.org/TR/html5/tokenization.html#tokenizing-character-references
msg188223 - (view) Author: Ezio Melotti (ezio.melotti) * (Python committer) Date: 2013-05-01 13:24
Another option is to add a new "convert_entities" option that, when True, automatically converts character references and doesn't call handle_charref and handle_entityref.  (See also #17802.)
msg203520 - (view) Author: Ezio Melotti (ezio.melotti) * (Python committer) Date: 2013-11-20 18:46
Here is a patch.
It might be also be a good idea to add warning when the option is not explicitly set to False, and change the default to True in 3.5/3.6.
msg203836 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2013-11-22 19:01
I have added a couple of nitpicks on Rietveld. You can ignore most of them. ;)
msg204041 - (view) Author: Ezio Melotti (ezio.melotti) * (Python committer) Date: 2013-11-23 15:44
New patch attached.
msg204065 - (view) Author: Roundup Robot (python-dev) Date: 2013-11-23 17:52
New changeset 1575f2dd08c4 by Ezio Melotti in branch 'default':
#13633: Added a new convert_charrefs keyword arg to HTMLParser that, when True, automatically converts all character references.
http://hg.python.org/cpython/rev/1575f2dd08c4
msg204068 - (view) Author: Ezio Melotti (ezio.melotti) * (Python committer) Date: 2013-11-23 18:17
Fixed, thanks for the reviews!
History
Date User Action Args
2013-11-23 18:17:29ezio.melottisetstatus: open -> closed
resolution: fixed
messages: + msg204068

stage: commit review -> committed/rejected
2013-11-23 17:52:19python-devsetnosy: + python-dev
messages: + msg204065
2013-11-23 15:44:25ezio.melottisetfiles: + issue13633-2.diff

messages: + msg204041
stage: patch review -> commit review
2013-11-22 19:01:38serhiy.storchakasetmessages: + msg203836
2013-11-20 21:44:03eli.benderskysetnosy: - eli.bendersky
2013-11-20 19:17:37ezio.melottisettitle: Handling of hex character references in HTMLParser.handle_charref -> Automatically convert character references in HTMLParser
2013-11-20 18:46:21ezio.melottisetfiles: + issue13633.diff

nosy: + serhiy.storchaka
messages: + msg203520

keywords: + patch
stage: test needed -> patch review
2013-11-18 10:40:10ezio.melottisetdependencies: + expose html.parser.unescape
2013-11-17 02:37:38ezio.melottisetnosy: + r.david.murray, eli.bendersky
2013-05-01 13:24:37ezio.melottisetmessages: + msg188223
2012-10-06 03:38:58ezio.melottisetversions: + Python 3.4, - Python 2.7, Python 3.2, Python 3.3
2012-02-23 02:38:49ezio.melottisetdependencies: + html.entities mapping dicts need updating?
messages: + msg154036
2011-12-19 06:55:56ezio.melotticreate