PEP: 100
Title: Python Unicode Integration
Version: $Revision$
Last-Modified: $Date$
Author: mal@lemburg.com (Marc-André Lemburg)
Status: Final
Type: Standards Track
Content-Type: text/x-rst
Created: 10-Mar-2000
Python-Version: 2.0
Post-History:


Historical Note
===============

This document was first written by Marc-Andre in the pre-PEP days,
and was originally distributed as Misc/unicode.txt in Python
distributions up to and included Python 2.1.  The last revision of
the proposal in that location was labeled version 1.7 (CVS
revision 3.10).  Because the document clearly serves the purpose
of an informational PEP in the post-PEP era, it has been moved
here and reformatted to comply with PEP guidelines.  Future
revisions will be made to this document, while Misc/unicode.txt
will contain a pointer to this PEP.

-Barry Warsaw, PEP editor


Introduction
============

The idea of this proposal is to add native Unicode 3.0 support to
Python in a way that makes use of Unicode strings as simple as
possible without introducing too many pitfalls along the way.

Since this goal is not easy to achieve -- strings being one of the
most fundamental objects in Python -- we expect this proposal to
undergo some significant refinements.

Note that the current version of this proposal is still a bit
unsorted due to the many different aspects of the Unicode-Python
integration.

The latest version of this document is always available at:
http://starship.python.net/~lemburg/unicode-proposal.txt

Older versions are available as:
http://starship.python.net/~lemburg/unicode-proposal-X.X.txt

[ed. note: new revisions should be made to this PEP document,
while the historical record previous to version 1.7 should be
retrieved from MAL's url, or Misc/unicode.txt]


Conventions
===========

- In examples we use u = Unicode object and s = Python string

- 'XXX' markings indicate points of discussion (PODs)


General Remarks
===============

- Unicode encoding names should be lower case on output and
  case-insensitive on input (they will be converted to lower case
  by all APIs taking an encoding name as input).

- Encoding names should follow the name conventions as used by the
  Unicode Consortium: spaces are converted to hyphens, e.g. 'utf
  16' is written as 'utf-16'.

- Codec modules should use the same names, but with hyphens
  converted to underscores, e.g. ``utf_8``, ``utf_16``, ``iso_8859_1``.


Unicode Default Encoding
========================

The Unicode implementation has to make some assumption about the
encoding of 8-bit strings passed to it for coercion and about the
encoding to as default for conversion of Unicode to strings when
no specific encoding is given.  This encoding is called <default
encoding> throughout this text.

For this, the implementation maintains a global which can be set
in the site.py Python startup script.  Subsequent changes are not
possible.  The <default encoding> can be set and queried using the
two sys module APIs:

``sys.setdefaultencoding(encoding)``
   Sets the <default encoding> used by the Unicode implementation.
   encoding has to be an encoding which is supported by the
   Python installation, otherwise, a LookupError is raised.

   Note: This API is only available in site.py!  It is
   removed from the sys module by site.py after usage.

``sys.getdefaultencoding()``
   Returns the current <default encoding>.

If not otherwise defined or set, the <default encoding> defaults
to 'ascii'.  This encoding is also the startup default of Python
(and in effect before site.py is executed).

Note that the default site.py startup module contains disabled
optional code which can set the <default encoding> according to
the encoding defined by the current locale.  The locale module is
used to extract the encoding from the locale default settings
defined by the OS environment (see locale.py).  If the encoding
cannot be determined, is unknown or unsupported, the code defaults
to setting the <default encoding> to 'ascii'.  To enable this
code, edit the site.py file or place the appropriate code into the
sitecustomize.py module of your Python installation.


Unicode Constructors
====================

Python should provide a built-in constructor for Unicode strings
which is available through ``__builtins__``::

    u = unicode(encoded_string[,encoding=<default encoding>][,errors="strict"])

    u = u'<unicode-escape encoded Python string>'

    u = ur'<raw-unicode-escape encoded Python string>'

With the 'unicode-escape' encoding being defined as:

- all non-escape characters represent themselves as Unicode
  ordinal (e.g. 'a' -> U+0061).

- all existing defined Python escape sequences are interpreted as
  Unicode ordinals; note that ``\xXXXX`` can represent all Unicode
  ordinals, and ``\OOO`` (octal) can represent Unicode ordinals up to
  U+01FF.

- a new escape sequence, ``\uXXXX``, represents U+XXXX; it is a syntax
  error to have fewer than 4 digits after ``\u``.

For an explanation of possible values for errors see the Codec
section below.

Examples::

    u'abc'          -> U+0061 U+0062 U+0063
    u'\u1234'       -> U+1234
    u'abc\u1234\n'  -> U+0061 U+0062 U+0063 U+1234 U+005c

The 'raw-unicode-escape' encoding is defined as follows:

- ``\uXXXX`` sequence represent the U+XXXX Unicode character if and
  only if the number of leading backslashes is odd

- all other characters represent themselves as Unicode ordinal
  (e.g. 'b' -> U+0062)

Note that you should provide some hint to the encoding you used to
write your programs as pragma line in one the first few comment
lines of the source file (e.g. '# source file encoding: latin-1').
If you only use 7-bit ASCII then everything is fine and no such
notice is needed, but if you include Latin-1 characters not
defined in ASCII, it may well be worthwhile including a hint since
people in other countries will want to be able to read your source
strings too.


Unicode Type Object
===================

Unicode objects should have the type UnicodeType with type name
'unicode', made available through the standard types module.


Unicode Output
==============

Unicode objects have a method .encode([encoding=<default encoding>])
which returns a Python string encoding the Unicode string using the
given scheme (see Codecs).

::

    print u := print u.encode()   # using the <default encoding>

    str(u)  := u.encode()         # using the <default encoding>

    repr(u) := "u%s" % repr(u.encode('unicode-escape'))

Also see Internal Argument Parsing and Buffer Interface for
details on how other APIs written in C will treat Unicode objects.


Unicode Ordinals
================

Since Unicode 3.0 has a 32-bit ordinal character set, the
implementation should provide 32-bit aware ordinal conversion
APIs::

    ord(u[:1]) (this is the standard ord() extended to work with Unicode
                objects)
      --> Unicode ordinal number (32-bit)

    unichr(i)
        --> Unicode object for character i (provided it is 32-bit);
            ValueError otherwise

Both APIs should go into ``__builtins__`` just like their string
counterparts ``ord()`` and ``chr()``.

Note that Unicode provides space for private encodings.  Usage of
these can cause different output representations on different
machines.  This problem is not a Python or Unicode problem, but a
machine setup and maintenance one.


Comparison & Hash Value
=======================

Unicode objects should compare equal to other objects after these
other objects have been coerced to Unicode.  For strings this
means that they are interpreted as Unicode string using the
<default encoding>.

Unicode objects should return the same hash value as their ASCII
equivalent strings.  Unicode strings holding non-ASCII values are
not guaranteed to return the same hash values as the default
encoded equivalent string representation.

When compared using ``cmp()`` (or ``PyObject_Compare()``) the
implementation should mask ``TypeErrors`` raised during the conversion
to remain in synch with the string behavior.  All other errors
such as ``ValueErrors`` raised during coercion of strings to Unicode
should not be masked and passed through to the user.

In containment tests ('a' in u'abc' and u'a' in 'abc') both sides
should be coerced to Unicode before applying the test.  Errors
occurring during coercion (e.g. None in u'abc') should not be
masked.


Coercion
========

Using Python strings and Unicode objects to form new objects
should always coerce to the more precise format, i.e. Unicode
objects.

::

    u + s := u + unicode(s)

    s + u := unicode(s) + u

All string methods should delegate the call to an equivalent
Unicode object method call by converting all involved strings to
Unicode and then applying the arguments to the Unicode method of
the same name, e.g.

::

    string.join((s,u),sep) := (s + sep) + u

    sep.join((s,u)) := (s + sep) + u

For a discussion of %-formatting w/r to Unicode objects, see
Formatting Markers.


Exceptions
==========

``UnicodeError`` is defined in the exceptions module as a subclass of
``ValueError``.  It is available at the C level via
``PyExc_UnicodeError``.  All exceptions related to Unicode
encoding/decoding should be subclasses of ``UnicodeError``.


Codecs (Coder/Decoders) Lookup
==============================

A Codec (see Codec Interface Definition) search registry should be
implemented by a module "codecs"::

    codecs.register(search_function)

Search functions are expected to take one argument, the encoding
name in all lower case letters and with hyphens and spaces
converted to underscores, and return a tuple of functions
(encoder, decoder, stream_reader, stream_writer) taking the
following arguments:

encoder and decoder
    These must be functions or methods which have the same
    interface as the ``.encode``/``.decode`` methods of Codec instances
    (see Codec Interface). The functions/methods are expected to
    work in a stateless mode.

stream_reader and stream_writer
    These need to be factory functions with the following
    interface::

        factory(stream,errors='strict')

    The factory functions must return objects providing the
    interfaces defined by ``StreamWriter``/``StreamReader`` resp.  (see
    Codec Interface).  Stream codecs can maintain state.

    Possible values for errors are defined in the Codec section
    below.

In case a search function cannot find a given encoding, it should
return None.

Aliasing support for encodings is left to the search functions to
implement.

The codecs module will maintain an encoding cache for performance
reasons.  Encodings are first looked up in the cache.  If not
found, the list of registered search functions is scanned.  If no
codecs tuple is found, a LookupError is raised.  Otherwise, the
codecs tuple is stored in the cache and returned to the caller.

To query the Codec instance the following API should be used::

    codecs.lookup(encoding)

This will either return the found codecs tuple or raise a
``LookupError``.


Standard Codecs
===============

Standard codecs should live inside an encodings/ package directory
in the Standard Python Code Library.  The ``__init__.py`` file of that
directory should include a Codec Lookup compatible search function
implementing a lazy module based codec lookup.

Python should provide a few standard codecs for the most relevant
encodings, e.g.

::

    'utf-8':              8-bit variable length encoding
    'utf-16':             16-bit variable length encoding (little/big endian)
    'utf-16-le':          utf-16 but explicitly little endian
    'utf-16-be':          utf-16 but explicitly big endian
    'ascii':              7-bit ASCII codepage
    'iso-8859-1':         ISO 8859-1 (Latin 1) codepage
    'unicode-escape':     See Unicode Constructors for a definition
    'raw-unicode-escape': See Unicode Constructors for a definition
    'native':             Dump of the Internal Format used by Python

Common aliases should also be provided per default, e.g.
'latin-1' for 'iso-8859-1'.

Note: 'utf-16' should be implemented by using and requiring byte
order marks (BOM) for file input/output.

All other encodings such as the CJK ones to support Asian scripts
should be implemented in separate packages which do not get
included in the core Python distribution and are not a part of
this proposal.


Codecs Interface Definition
===========================

The following base class should be defined in the module "codecs".
They provide not only templates for use by encoding module
implementors, but also define the interface which is expected by
the Unicode implementation.

Note that the Codec Interface defined here is well suitable for a
larger range of applications.  The Unicode implementation expects
Unicode objects on input for ``.encode()`` and ``.write()`` and character
buffer compatible objects on input for ``.decode()``.  Output of
``.encode()`` and ``.read()`` should be a Python string and ``.decode()`` must
return an Unicode object.

First, we have the stateless encoders/decoders.  These do not work
in chunks as the stream codecs (see below) do, because all
components are expected to be available in memory.

::

    class Codec:

        """Defines the interface for stateless encoders/decoders.

           The .encode()/.decode() methods may implement different
           error handling schemes by providing the errors argument.
           These string values are defined:

             'strict'  - raise an error (or a subclass)
             'ignore'  - ignore the character and continue with the next
             'replace' - replace with a suitable replacement character;
                         Python will use the official U+FFFD
                         REPLACEMENT CHARACTER for the builtin Unicode
                         codecs.
        """

        def encode(self,input,errors='strict'):

            """Encodes the object input and returns a tuple (output
               object, length consumed).

               errors defines the error handling to apply.  It
               defaults to 'strict' handling.

               The method may not store state in the Codec instance.
               Use StreamCodec for codecs which have to keep state in
               order to make encoding/decoding efficient.
            """

        def decode(self,input,errors='strict'):

            """Decodes the object input and returns a tuple (output
               object, length consumed).

               input must be an object which provides the
               bf_getreadbuf buffer slot.  Python strings, buffer
               objects and memory mapped files are examples of objects
               providing this slot.

               errors defines the error handling to apply.  It
               defaults to 'strict' handling.

               The method may not store state in the Codec instance.
               Use StreamCodec for codecs which have to keep state in
               order to make encoding/decoding efficient.

            """

``StreamWriter`` and ``StreamReader`` define the interface for stateful
encoders/decoders which work on streams.  These allow processing
of the data in chunks to efficiently use memory.  If you have
large strings in memory, you may want to wrap them with ``cStringIO``
objects and then use these codecs on them to be able to do chunk
processing as well, e.g. to provide progress information to the
user.

::

    class StreamWriter(Codec):

        def __init__(self,stream,errors='strict'):

            """Creates a StreamWriter instance.

               stream must be a file-like object open for writing
               (binary) data.

               The StreamWriter may implement different error handling
               schemes by providing the errors keyword argument.
               These parameters are defined:

                 'strict' - raise a ValueError (or a subclass)
                 'ignore' - ignore the character and continue with the next
                 'replace'- replace with a suitable replacement character
            """
            self.stream = stream
            self.errors = errors

        def write(self,object):

            """Writes the object's contents encoded to self.stream.
            """
            data, consumed = self.encode(object,self.errors)
            self.stream.write(data)

        def writelines(self, list):

            """Writes the concatenated list of strings to the stream
               using .write().
            """
            self.write(''.join(list))

        def reset(self):

            """Flushes and resets the codec buffers used for keeping state.

               Calling this method should ensure that the data on the
               output is put into a clean state, that allows appending
               of new fresh data without having to rescan the whole
               stream to recover state.
            """
            pass

        def __getattr__(self,name, getattr=getattr):

            """Inherit all other methods from the underlying stream.
            """
            return getattr(self.stream,name)


    class StreamReader(Codec):

        def __init__(self,stream,errors='strict'):

            """Creates a StreamReader instance.

               stream must be a file-like object open for reading
               (binary) data.

               The StreamReader may implement different error handling
               schemes by providing the errors keyword argument.
               These parameters are defined:

                 'strict' - raise a ValueError (or a subclass)
                 'ignore' - ignore the character and continue with the next
                 'replace'- replace with a suitable replacement character;
            """
            self.stream = stream
            self.errors = errors

        def read(self,size=-1):

            """Decodes data from the stream self.stream and returns the
               resulting object.

               size indicates the approximate maximum number of bytes
               to read from the stream for decoding purposes.  The
               decoder can modify this setting as appropriate.  The
               default value -1 indicates to read and decode as much
               as possible.  size is intended to prevent having to
               decode huge files in one step.

               The method should use a greedy read strategy meaning
               that it should read as much data as is allowed within
               the definition of the encoding and the given size, e.g.
               if optional encoding endings or state markers are
               available on the stream, these should be read too.
            """
            # Unsliced reading:
            if size < 0:
                return self.decode(self.stream.read())[0]

            # Sliced reading:
            read = self.stream.read
            decode = self.decode
            data = read(size)
            i = 0
            while 1:
                try:
                    object, decodedbytes = decode(data)
                except ValueError,why:
                    # This method is slow but should work under pretty
                    # much all conditions; at most 10 tries are made
                    i = i + 1
                    newdata = read(1)
                    if not newdata or i > 10:
                        raise
                    data = data + newdata
                else:
                    return object

        def readline(self, size=None):

            """Read one line from the input stream and return the
               decoded data.

               Note: Unlike the .readlines() method, this method
               inherits the line breaking knowledge from the
               underlying stream's .readline() method -- there is
               currently no support for line breaking using the codec
               decoder due to lack of line buffering.  Subclasses
               should however, if possible, try to implement this
               method using their own knowledge of line breaking.

               size, if given, is passed as size argument to the
               stream's .readline() method.
            """
            if size is None:
                line = self.stream.readline()
            else:
                line = self.stream.readline(size)
            return self.decode(line)[0]

        def readlines(self, sizehint=0):

            """Read all lines available on the input stream
               and return them as list of lines.

               Line breaks are implemented using the codec's decoder
               method and are included in the list entries.

               sizehint, if given, is passed as size argument to the
               stream's .read() method.
            """
            if sizehint is None:
                data = self.stream.read()
            else:
                data = self.stream.read(sizehint)
            return self.decode(data)[0].splitlines(1)

        def reset(self):

            """Resets the codec buffers used for keeping state.

               Note that no stream repositioning should take place.
               This method is primarily intended to be able to recover
               from decoding errors.

            """
            pass

        def __getattr__(self,name, getattr=getattr):

            """ Inherit all other methods from the underlying stream.
            """
            return getattr(self.stream,name)


Stream codec implementors are free to combine the ``StreamWriter`` and
``StreamReader`` interfaces into one class.  Even combining all these
with the Codec class should be possible.

Implementors are free to add additional methods to enhance the
codec functionality or provide extra state information needed for
them to work.  The internal codec implementation will only use the
above interfaces, though.

It is not required by the Unicode implementation to use these base
classes, only the interfaces must match; this allows writing
Codecs as extension types.

As guideline, large mapping tables should be implemented using
static C data in separate (shared) extension modules.  That way
multiple processes can share the same data.

A tool to auto-convert Unicode mapping files to mapping modules
should be provided to simplify support for additional mappings
(see References).


Whitespace
==========

The ``.split()`` method will have to know about what is considered
whitespace in Unicode.


Case Conversion
===============

Case conversion is rather complicated with Unicode data, since
there are many different conditions to respect.  See

    http://www.unicode.org/unicode/reports/tr13/

for some guidelines on implementing case conversion.

For Python, we should only implement the 1-1 conversions included
in Unicode.  Locale dependent and other special case conversions
(see the Unicode standard file SpecialCasing.txt) should be left
to user land routines and not go into the core interpreter.

The methods ``.capitalize()`` and ``.iscapitalized()`` should follow the
case mapping algorithm defined in the above technical report as
closely as possible.


Line Breaks
===========

Line breaking should be done for all Unicode characters having the
B property as well as the combinations CRLF, CR, LF (interpreted
in that order) and other special line separators defined by the
standard.

The Unicode type should provide a ``.splitlines()`` method which
returns a list of lines according to the above specification. See
Unicode Methods.


Unicode Character Properties
============================

A separate module "unicodedata" should provide a compact interface
to all Unicode character properties defined in the standard's
UnicodeData.txt file.

Among other things, these properties provide ways to recognize
numbers, digits, spaces, whitespace, etc.

Since this module will have to provide access to all Unicode
characters, it will eventually have to contain the data from
UnicodeData.txt which takes up around 600kB.  For this reason, the
data should be stored in static C data.  This enables compilation
as shared module which the underlying OS can shared between
processes (unlike normal Python code modules).

There should be a standard Python interface for accessing this
information so that other implementors can plug in their own
possibly enhanced versions, e.g. ones that do decompressing of the
data on-the-fly.


Private Code Point Areas
========================

Support for these is left to user land Codecs and not explicitly
integrated into the core.  Note that due to the Internal Format
being implemented, only the area between ``\uE000`` and ``\uF8FF`` is
usable for private encodings.


Internal Format
===============

The internal format for Unicode objects should use a Python
specific fixed format <PythonUnicode> implemented as 'unsigned
short' (or another unsigned numeric type having 16 bits).  Byte
order is platform dependent.

This format will hold UTF-16 encodings of the corresponding
Unicode ordinals.  The Python Unicode implementation will address
these values as if they were UCS-2 values. UCS-2 and UTF-16 are
the same for all currently defined Unicode character points.
UTF-16 without surrogates provides access to about 64k characters
and covers all characters in the Basic Multilingual Plane (BMP) of
Unicode.

It is the Codec's responsibility to ensure that the data they pass
to the Unicode object constructor respects this assumption.  The
constructor does not check the data for Unicode compliance or use
of surrogates.

Future implementations can extend the 32 bit restriction to the
full set of all UTF-16 addressable characters (around 1M
characters).

The Unicode API should provide interface routines from
<PythonUnicode> to the compiler's wchar_t which can be 16 or 32
bit depending on the compiler/libc/platform being used.

Unicode objects should have a pointer to a cached Python string
object <defenc> holding the object's value using the <default
encoding>.  This is needed for performance and internal parsing
(see Internal Argument Parsing) reasons.  The buffer is filled
when the first conversion request to the <default encoding> is
issued on the object.

Interning is not needed (for now), since Python identifiers are
defined as being ASCII only.

``codecs.BOM`` should return the byte order mark (BOM) for the format
used internally.  The codecs module should provide the following
additional constants for convenience and reference (``codecs.BOM``
will either be ``BOM_BE`` or ``BOM_LE`` depending on the platform)::

    BOM_BE: '\376\377'
      (corresponds to Unicode U+0000FEFF in UTF-16 on big endian
       platforms == ZERO WIDTH NO-BREAK SPACE)

    BOM_LE: '\377\376'
      (corresponds to Unicode U+0000FFFE in UTF-16 on little endian
       platforms == defined as being an illegal Unicode character)

    BOM4_BE: '\000\000\376\377'
      (corresponds to Unicode U+0000FEFF in UCS-4)

    BOM4_LE: '\377\376\000\000'
      (corresponds to Unicode U+0000FFFE in UCS-4)

Note that Unicode sees big endian byte order as being "correct".
The swapped order is taken to be an indicator for a "wrong"
format, hence the illegal character definition.

The configure script should provide aid in deciding whether Python
can use the native ``wchar_t`` type or not (it has to be a 16-bit
unsigned type).


Buffer Interface
================

Implement the buffer interface using the <defenc> Python string
object as basis for ``bf_getcharbuf`` and the internal buffer for
``bf_getreadbuf``.  If ``bf_getcharbuf`` is requested and the <defenc>
object does not yet exist, it is created first.

Note that as special case, the parser marker "s#" will not return
raw Unicode UTF-16 data (which the ``bf_getreadbuf`` returns), but
instead tries to encode the Unicode object using the default
encoding and then returns a pointer to the resulting string object
(or raises an exception in case the conversion fails).  This was
done in order to prevent accidentely writing binary data to an
output stream which the other end might not recognize.

This has the advantage of being able to write to output streams
(which typically use this interface) without additional
specification of the encoding to use.

If you need to access the read buffer interface of Unicode
objects, use the ``PyObject_AsReadBuffer()`` interface.

The internal format can also be accessed using the
'unicode-internal' codec, e.g. via ``u.encode('unicode-internal')``.


Pickle/Marshalling
==================

Should have native Unicode object support.  The objects should be
encoded using platform independent encodings.

Marshal should use UTF-8 and Pickle should either choose
Raw-Unicode-Escape (in text mode) or UTF-8 (in binary mode) as
encoding.  Using UTF-8 instead of UTF-16 has the advantage of
eliminating the need to store a BOM mark.


Regular Expressions
===================

Secret Labs AB is working on a Unicode-aware regular expression
machinery.  It works on plain 8-bit, UCS-2, and (optionally) UCS-4
internal character buffers.

Also see

    http://www.unicode.org/unicode/reports/tr18/

for some remarks on how to treat Unicode REs.


Formatting Markers
==================

Format markers are used in Python format strings.  If Python
strings are used as format strings, the following interpretations
should be in effect::

    '%s': For Unicode objects this will cause coercion of the
          whole format string to Unicode.  Note that you should use
          a Unicode format string to start with for performance
          reasons.

In case the format string is an Unicode object, all parameters are
coerced to Unicode first and then put together and formatted
according to the format string.  Numbers are first converted to
strings and then to Unicode.

::

    '%s': Python strings are interpreted as Unicode
          string using the <default encoding>.  Unicode objects are
          taken as is.

All other string formatters should work accordingly.

Example::

    u"%s %s" % (u"abc", "abc")  ==  u"abc abc"


Internal Argument Parsing
=========================

These markers are used by the ``PyArg_ParseTuple()`` APIs:

"U"
    Check for Unicode object and return a pointer to it

"s"
   For Unicode objects: return a pointer to the object's
   <defenc> buffer (which uses the <default encoding>).

"s#"
   Access to the default encoded version of the Unicode object
   (see Buffer Interface); note that the length relates to
   the length of the default encoded string rather than the
   Unicode object length.

"t#"
    Same as "s#".

"es"
   Takes two parameters: encoding (``const char *``) and buffer
   (``char **``).

   The input object is first coerced to Unicode in the usual
   way and then encoded into a string using the given
   encoding.

   On output, a buffer of the needed size is allocated and
   returned through ``*buffer`` as NULL-terminated string.  The
   encoded may not contain embedded NULL characters.  The
   caller is responsible for calling ``PyMem_Free()`` to free the
   allocated ``*buffer`` after usage.

"es#"
    Takes three parameters: encoding (``const char *``), buffer
    (``char **``) and buffer_len (``int *``).

    The input object is first coerced to Unicode in the usual
    way and then encoded into a string using the given
    encoding.

    If ``*buffer`` is non-NULL, ``*buffer_len`` must be set to
    ``sizeof(buffer)`` on input. Output is then copied to ``*buffer``.

    If ``*buffer`` is NULL, a buffer of the needed size is
    allocated and output copied into it.  ``*buffer`` is then
    updated to point to the allocated memory area.  The caller
    is responsible for calling ``PyMem_Free()`` to free the
    allocated ``*buffer`` after usage.

    In both cases ``*buffer_len`` is updated to the number of
    characters written (excluding the trailing NULL-byte).
    The output buffer is assured to be NULL-terminated.

Examples:

Using "es#" with auto-allocation::

    static PyObject *
    test_parser(PyObject *self,
                PyObject *args)
    {
        PyObject *str;
        const char *encoding = "latin-1";
        char *buffer = NULL;
        int buffer_len = 0;

        if (!PyArg_ParseTuple(args, "es#:test_parser",
                              encoding, &buffer, &buffer_len))
            return NULL;
        if (!buffer) {
            PyErr_SetString(PyExc_SystemError,
                            "buffer is NULL");
            return NULL;
        }
        str = PyString_FromStringAndSize(buffer, buffer_len);
        PyMem_Free(buffer);
        return str;
    }

Using "es" with auto-allocation returning a NULL-terminated string::

    static PyObject *
    test_parser(PyObject *self,
                PyObject *args)
    {
        PyObject *str;
        const char *encoding = "latin-1";
        char *buffer = NULL;

        if (!PyArg_ParseTuple(args, "es:test_parser",
                              encoding, &buffer))
            return NULL;
        if (!buffer) {
            PyErr_SetString(PyExc_SystemError,
                            "buffer is NULL");
            return NULL;
        }
        str = PyString_FromString(buffer);
        PyMem_Free(buffer);
        return str;
    }

Using "es#" with a pre-allocated buffer::

    static PyObject *
    test_parser(PyObject *self,
                PyObject *args)
    {
        PyObject *str;
        const char *encoding = "latin-1";
        char _buffer[10];
        char *buffer = _buffer;
        int buffer_len = sizeof(_buffer);

        if (!PyArg_ParseTuple(args, "es#:test_parser",
                              encoding, &buffer, &buffer_len))
            return NULL;
        if (!buffer) {
            PyErr_SetString(PyExc_SystemError,
                            "buffer is NULL");
            return NULL;
        }
        str = PyString_FromStringAndSize(buffer, buffer_len);
        return str;
    }


File/Stream Output
==================

Since file.write(object) and most other stream writers use the
"s#" or "t#" argument parsing marker for querying the data to
write, the default encoded string version of the Unicode object
will be written to the streams (see Buffer Interface).

For explicit handling of files using Unicode, the standard stream
codecs as available through the codecs module should be used.

The codecs module should provide a short-cut
open(filename,mode,encoding) available which also assures that
mode contains the 'b' character when needed.


File/Stream Input
=================

Only the user knows what encoding the input data uses, so no
special magic is applied.  The user will have to explicitly
convert the string data to Unicode objects as needed or use the
file wrappers defined in the codecs module (see File/Stream
Output).


Unicode Methods & Attributes
============================

All Python string methods, plus::

    .encode([encoding=<default encoding>][,errors="strict"])
       --> see Unicode Output

    .splitlines([include_breaks=0])
       --> breaks the Unicode string into a list of (Unicode) lines;
           returns the lines with line breaks included, if
           include_breaks is true.  See Line Breaks for a
           specification of how line breaking is done.


Code Base
=========

We should use Fredrik Lundh's Unicode object implementation as
basis. It already implements most of the string methods needed
and provides a well written code base which we can build upon.

The object sharing implemented in Fredrik's implementation should
be dropped.


Test Cases
==========

Test cases should follow those in Lib/test/test_string.py and
include additional checks for the Codec Registry and the Standard
Codecs.


References
==========

* Unicode Consortium: http://www.unicode.org/

* Unicode FAQ: http://www.unicode.org/unicode/faq/

* Unicode 3.0: http://www.unicode.org/unicode/standard/versions/Unicode3.0.html

* Unicode-TechReports: http://www.unicode.org/unicode/reports/techreports.html

* Unicode-Mappings: ftp://ftp.unicode.org/Public/MAPPINGS/

* Introduction to Unicode (a little outdated by still nice to read):
  http://www.nada.kth.se/i18n/ucs/unicode-iso10646-oview.html

* For comparison:
  Introducing Unicode to ECMAScript (aka JavaScript) --
  http://www-4.ibm.com/software/developer/library/internationalization-support.html

* IANA Character Set Names:
  ftp://ftp.isi.edu/in-notes/iana/assignments/character-sets

* Discussion of UTF-8 and Unicode support for POSIX and Linux:
  http://www.cl.cam.ac.uk/~mgk25/unicode.html

* Encodings:

  * Overview: http://czyborra.com/utf/

  * UCS-2: http://www.uazone.org/multiling/unicode/ucs2.html

  * UTF-7: Defined in RFC2152, e.g.
    http://www.uazone.org/multiling/ml-docs/rfc2152.txt

  * UTF-8: Defined in RFC2279, e.g.
    https://tools.ietf.org/html/rfc2279

  * UTF-16: http://www.uazone.org/multiling/unicode/wg2n1035.html


History of this Proposal
========================

[ed. note: revisions prior to 1.7 are available in the CVS history
of Misc/unicode.txt from the standard Python distribution.  All
subsequent history is available via the CVS revisions on this
file.]

1.7
---

* Added note about the changed behaviour of "s#".

1.6
---

* Changed <defencstr> to <defenc> since this is the name used in the
  implementation.
* Added notes about the usage of <defenc> in
  the buffer protocol implementation.

1.5
---

* Added notes about setting the <default encoding>.
* Fixed some typos (thanks to Andrew Kuchling).
* Changed <defencstr> to <utf8str>.

1.4
---

* Added note about mixed type comparisons and contains tests.
* Changed treating of Unicode objects in format strings (if
  used with ``'%s' % u`` they will now cause the format string to
  be coerced to Unicode, thus producing a Unicode object on
  return).
* Added link to IANA charset names (thanks to Lars
  Marius Garshol).
* Added new codec methods ``.readline()``,
  ``.readlines()`` and ``.writelines()``.

1.3
---

* Added new "es" and "es#" parser markers

1.2
---

* Removed POD about ``codecs.open()``

1.1
---

* Added note about comparisons and hash values.
* Added note about case mapping algorithms.
* Changed stream codecs ``.read()`` and ``.write()`` method
  to match the standard file-like object
  methods (bytes consumed information is no longer returned by
  the methods)

1.0
---

* changed encode Codec method to be symmetric to the decode method
  (they both return (object, data consumed) now and thus become
  interchangeable);
* removed ``__init__`` method of Codec class (the
  methods are stateless) and moved the errors argument down to
  the methods;
* made the Codec design more generic w/r to type
  of input and output objects;
* changed ``StreamWriter.flush`` to ``StreamWriter.reset`` in order to
  avoid overriding the stream's ``.flush()`` method;
* renamed ``.breaklines()`` to ``.splitlines()``;
* renamed the module unicodec to codecs;
* modified the File I/O section to refer to the stream codecs.

0.9
---

* changed errors keyword argument definition;
* added 'replace' error handling;
* changed the codec APIs to accept buffer like
  objects on input;
* some minor typo fixes;
* added Whitespace section and included references for Unicode characters that
  have the whitespace and the line break characteristic;
* added note that search functions can expect lower-case encoding names;
* dropped slicing and offsets in the codec APIs

0.8
---

* added encodings package and raw unicode escape encoding;
* untabified the proposal;
* added notes on Unicode format strings;
* added ``.breaklines()`` method

0.7
---

* added a whole new set of codec APIs;
* added a different encoder lookup scheme;
* fixed some names

0.6
---

* changed "s#" to "t#";
* changed <defencbuf> to <defencstr> holding
  a real Python string object;
* changed Buffer Interface to
  delegate requests to <defencstr>'s buffer interface;
* removed the explicit reference to the unicodec.codecs dictionary (the
  module can implement this in way fit for the purpose);
* removed the settable default encoding;
* move ``UnicodeError`` from unicodec to exceptions;
* "s#" not returns the internal data;
* passed the UCS-2/UTF-16 checking from the Unicode constructor
  to the Codecs

0.5
---

* moved ``sys.bom`` to ``unicodec.BOM``;
* added sections on case mapping,
* private use encodings and Unicode character properties

0.4
---

* added Codec interface, notes on %-formatting,
* changed some encoding details,
* added comments on stream wrappers,
* fixed some discussion points (most important: Internal Format),
* clarified the 'unicode-escape' encoding, added encoding
  references

0.3
---

* added references, comments on codec modules, the internal format,
  bf_getcharbuffer and the RE engine;
* added 'unicode-escape'
  encoding proposed by Tim Peters and fixed repr(u) accordingly

0.2
---

* integrated Guido's suggestions, added stream codecs and file wrapping

0.1
---

* first version


..
   Local Variables:
   mode: indented-text
   indent-tabs-mode: nil
   End:
