[Python-ideas] Processing surrogates in
Steven D'Aprano
steve at pearwood.info
Tue May 5 19:28:46 CEST 2015
On Mon, May 04, 2015 at 11:15:47AM +0300, Serhiy Storchaka wrote:
> Surrogate characters (U+D800-U+DFFF) are not allowed in Unicode, but
> Python allows them in Unicode strings for different purposes.
>
> 1) To represent UTF-8, UTF-16 or UTF-32 encoded strings that contain
> surrogate characters. This data can came from other programs, including
> Python 2.
Can you give a simple example of a Python 2 program that provides output
that Python 3 will read as surrogates?
> 2) To represent undecodable bytes in ASCII-compatible encoding with the
> "surrogateescape" error handlers.
>
> So surrogate characters can be obtained from "surrogateescape" or
> "surrogatepass" error handlers or created manually with chr() or %c.
>
> Some encodings (UTF-7, unicode-escape) also allows surrogate characters.
Also UTF-16, and possible others.
I'm not entirely sure, but I think that this is a mistake, if not a
bug. I think that *no* UTF encoding should allow lone surrogates to
escape through encoding. But I not entirely sure, so I won't argue that
now -- besides, it's irrelevant to the proposal.
> But on output the surrogate characters can cause fail.
What do you mean by "on output"? Do you mean when printing?
> In issue18814 proposed several functions to work with surrogate and
> astral characters. All these functions takes a string and returns a string.
I like the idea of having better surrogate and astral character
handling, but I don't think I like your suggested API of using functions
for this. I think this is better handled as str-to-str codecs.
Unfortunately, there is still no concensus of the much-debated return of
str-to-str and byte-to-byte codecs via the str.encode and byte.decode
methods. At one point people were talking about adding a separate method
(transform?) to handle them, but that seems to have been forgotten.
Fortunately the codecs module handles them just fine:
py> codecs.encode("Hello world", "rot-13")
'Uryyb jbeyq'
I propose, instead of your function/method rehandle_surrogatepass(), we
add a pair of str-to-str codecs:
codecs.encode(mystring, 'remove_surrogates', errors='strict')
codecs.encode(mystring, 'remove_astrals', errors='strict')
For the first one, if the string has no surrogates, it returns the
string unchanged. If it contains any surrogates, the error handler runs
in the usual fashion.
The second is exactly the same, except it checks for astral characters.
For the avoidance of doubt:
* surrogates are code points in the range U+D800 to U+DFFF inclusive;
* astrals are characters from the Supplementary Multilingual Planes,
that is code points U+10000 and above.
Advantage of using codecs:
- there's no arguments about where to put it (is it a str method? a
function? in the string module? some other module? where?)
- we can use the usual codec machinery, rather than duplicate it;
- people already understand that codecs and error handles go together;
Disadvantage:
- have to use codec.encode instead of str.encode.
It is slightly sad that there is still no entirely obvious way to call
str-to-str codecs from the encode method, but since this is a fairly
advanced and unusual use-case, I don't think it is a problem that we
have to use the codecs module.
> * decompose_astrals(string)
> * compose_surrogate_pairs(string)
I'm not sure about those. I have to think about them.
--
Steve
More information about the Python-ideas
mailing list