Index: /pyyaml/trunk/lib/yaml/__init__.py
===================================================================
--- /pyyaml/trunk/lib/yaml/__init__.py	(revision 314)
+++ /pyyaml/trunk/lib/yaml/__init__.py	(revision 333)
@@ -92,8 +92,5 @@
     getvalue = None
     if stream is None:
-        try:
-            from cStringIO import StringIO
-        except ImportError:
-            from StringIO import StringIO
+        from StringIO import StringIO
         stream = StringIO()
         getvalue = stream.getvalue
@@ -116,8 +113,8 @@
     getvalue = None
     if stream is None:
-        try:
+        if encoding is None:
+            from StringIO import StringIO
+        else:
             from cStringIO import StringIO
-        except ImportError:
-            from StringIO import StringIO
         stream = StringIO()
         getvalue = stream.getvalue
@@ -152,8 +149,8 @@
     getvalue = None
     if stream is None:
-        try:
+        if encoding is None:
+            from StringIO import StringIO
+        else:
             from cStringIO import StringIO
-        except ImportError:
-            from StringIO import StringIO
         stream = StringIO()
         getvalue = stream.getvalue
Index: /pyyaml/trunk/lib/yaml/cyaml.py
===================================================================
--- /pyyaml/trunk/lib/yaml/cyaml.py	(revision 223)
+++ /pyyaml/trunk/lib/yaml/cyaml.py	(revision 333)
@@ -42,5 +42,5 @@
             version=None, tags=None):
         CEmitter.__init__(self, stream, canonical=canonical,
-                indent=indent, width=width,
+                indent=indent, width=width, encoding=encoding,
                 allow_unicode=allow_unicode, line_break=line_break,
                 explicit_start=explicit_start, explicit_end=explicit_end,
@@ -59,5 +59,5 @@
             version=None, tags=None):
         CEmitter.__init__(self, stream, canonical=canonical,
-                indent=indent, width=width,
+                indent=indent, width=width, encoding=encoding,
                 allow_unicode=allow_unicode, line_break=line_break,
                 explicit_start=explicit_start, explicit_end=explicit_end,
@@ -76,5 +76,5 @@
             version=None, tags=None):
         CEmitter.__init__(self, stream, canonical=canonical,
-                indent=indent, width=width,
+                indent=indent, width=width, encoding=encoding,
                 allow_unicode=allow_unicode, line_break=line_break,
                 explicit_start=explicit_start, explicit_end=explicit_end,
Index: /pyyaml/trunk/lib/yaml/emitter.py
===================================================================
--- /pyyaml/trunk/lib/yaml/emitter.py	(revision 328)
+++ /pyyaml/trunk/lib/yaml/emitter.py	(revision 333)
@@ -155,5 +155,5 @@
     def expect_stream_start(self):
         if isinstance(self.event, StreamStartEvent):
-            if self.event.encoding:
+            if self.event.encoding and not getattr(self.stream, 'encoding', None):
                 self.encoding = self.event.encoding
             self.write_stream_start()
Index: /pyyaml/trunk/ext/_yaml.pyx
===================================================================
--- /pyyaml/trunk/ext/_yaml.pyx	(revision 331)
+++ /pyyaml/trunk/ext/_yaml.pyx	(revision 333)
@@ -250,4 +250,8 @@
     cdef object current_event
     cdef object anchors
+    cdef object stream_cache
+    cdef int stream_cache_len
+    cdef int stream_cache_pos
+    cdef int unicode_source
 
     def __init__(self, stream):
@@ -261,4 +265,5 @@
         except AttributeError:
             is_readable = 0
+        self.unicode_source = 0
         if is_readable:
             self.stream = stream
@@ -267,4 +272,7 @@
             except AttributeError:
                 self.stream_name = '<file>'
+            self.stream_cache = None
+            self.stream_cache_len = 0
+            self.stream_cache_pos = 0
             yaml_parser_set_input(&self.parser, input_handler, <void *>self)
         else:
@@ -272,6 +280,7 @@
                 stream = PyUnicode_AsUTF8String(stream)
                 self.stream_name = '<unicode string>'
+                self.unicode_source = 1
             else:
-                self.stream_name = '<string>'
+                self.stream_name = '<byte string>'
             if PyString_CheckExact(stream) == 0:
                 raise TypeError("a string or stream input is required")
@@ -364,5 +373,6 @@
             encoding = None
             if token.data.stream_start.encoding == YAML_UTF8_ENCODING:
-                encoding = u"utf-8"
+                if self.unicode_source == 0:
+                    encoding = u"utf-8"
             elif token.data.stream_start.encoding == YAML_UTF16LE_ENCODING:
                 encoding = u"utf-16-le"
@@ -516,5 +526,6 @@
             encoding = None
             if event.data.stream_start.encoding == YAML_UTF8_ENCODING:
-                encoding = "utf-8"
+                if self.unicode_source == 0:
+                    encoding = "utf-8"
             elif event.data.stream_start.encoding == YAML_UTF16LE_ENCODING:
                 encoding = "utf-16-le"
@@ -878,13 +889,23 @@
     cdef CParser parser
     parser = <CParser>data
-    value = parser.stream.read(size)
-    if PyUnicode_CheckExact(value) != 0:
-        value = PyUnicode_AsUTF8String(value)
-    if PyString_CheckExact(value) == 0:
-        raise TypeError("a string value is expected")
-    if PyString_GET_SIZE(value) > size:
-        raise ValueError("a string value it too long")
-    memcpy(buffer, PyString_AS_STRING(value), PyString_GET_SIZE(value))
-    read[0] = PyString_GET_SIZE(value)
+    if parser.stream_cache is None:
+        value = parser.stream.read(size)
+        if PyUnicode_CheckExact(value) != 0:
+            value = PyUnicode_AsUTF8String(value)
+            parser.unicode_source = 1
+        if PyString_CheckExact(value) == 0:
+            raise TypeError("a string value is expected")
+        parser.stream_cache = value
+        parser.stream_cache_pos = 0
+        parser.stream_cache_len = PyString_GET_SIZE(value)
+    if (parser.stream_cache_len - parser.stream_cache_pos) < size:
+        size = parser.stream_cache_len - parser.stream_cache_pos
+    if size > 0:
+        memcpy(buffer, PyString_AS_STRING(parser.stream_cache)
+                            + parser.stream_cache_pos, size)
+    read[0] = size
+    parser.stream_cache_pos += size
+    if parser.stream_cache_pos == parser.stream_cache_len:
+        parser.stream_cache = None
     return 1
 
@@ -895,5 +916,4 @@
     cdef object stream
 
-    cdef yaml_encoding_t use_encoding
     cdef int document_start_implicit
     cdef int document_end_implicit
@@ -905,5 +925,6 @@
     cdef int last_alias_id
     cdef int closed
-    cdef int decode_output
+    cdef int dump_unicode
+    cdef object use_encoding
 
     def __init__(self, stream, canonical=None, indent=None, width=None,
@@ -913,11 +934,13 @@
             raise MemoryError
         self.stream = stream
-        self.decode_output = 1
+        self.dump_unicode = 0
         try:
-            stream.encoding
+            if stream.encoding:
+                self.dump_unicode = 1
         except AttributeError:
-            self.decode_output = 0
+            pass
+        self.use_encoding = encoding
         yaml_emitter_set_output(&self.emitter, output_handler, <void *>self)    
-        if canonical is not None:
+        if canonical:
             yaml_emitter_set_canonical(&self.emitter, 1)
         if indent is not None:
@@ -925,5 +948,5 @@
         if width is not None:
             yaml_emitter_set_width(&self.emitter, width)
-        if allow_unicode is not None:
+        if allow_unicode:
             yaml_emitter_set_unicode(&self.emitter, 1)
         if line_break is not None:
@@ -934,10 +957,4 @@
             elif line_break == '\r\n':
                 yaml_emitter_set_break(&self.emitter, YAML_CRLN_BREAK)
-        if encoding == 'utf-16-le':
-            self.use_encoding = YAML_UTF16LE_ENCODING
-        elif encoding == 'utf-16-be':
-            self.use_encoding = YAML_UTF16BE_ENCODING
-        else:
-            self.use_encoding = YAML_UTF8_ENCODING
         self.document_start_implicit = 1
         if explicit_start:
@@ -987,4 +1004,8 @@
             elif event_object.encoding == 'utf-16-be':
                 encoding = YAML_UTF16BE_ENCODING
+            if event_object.encoding is None:
+                self.dump_unicode = 1
+            if self.dump_unicode == 1:
+                encoding = YAML_UTF8_ENCODING
             yaml_stream_start_event_initialize(event, encoding)
         elif event_class is StreamEndEvent:
@@ -1151,6 +1172,17 @@
     def open(self):
         cdef yaml_event_t event
+        cdef yaml_encoding_t encoding
         if self.closed == -1:
-            yaml_stream_start_event_initialize(&event, self.use_encoding)
+            if self.use_encoding == 'utf-16-le':
+                encoding = YAML_UTF16LE_ENCODING
+            elif self.use_encoding == 'utf-16-be':
+                encoding = YAML_UTF16BE_ENCODING
+            else:
+                encoding = YAML_UTF8_ENCODING
+            if self.use_encoding is None:
+                self.dump_unicode = 1
+            if self.dump_unicode == 1:
+                encoding = YAML_UTF8_ENCODING
+            yaml_stream_start_event_initialize(&event, encoding)
             if yaml_emitter_emit(&self.emitter, &event) == 0:
                 error = self._emitter_error()
@@ -1374,5 +1406,5 @@
     cdef CEmitter emitter
     emitter = <CEmitter>data
-    if emitter.decode_output == 0:
+    if emitter.dump_unicode == 0:
         value = PyString_FromStringAndSize(buffer, size)
     else:
Index: /pyyaml/trunk/tests/data/invalid-character.stream-error
===================================================================
--- /pyyaml/trunk/tests/data/invalid-character.stream-error	(revision 322)
+++ /pyyaml/trunk/tests/data/invalid-character.stream-error	(revision 333)
@@ -1,18 +1,66 @@
-***************************************************************
-***************************************************************
-***************************************************************
-***************************************************************
-***************************************************************
-***************************************************************
-***************************************************************
-***************************************************************
-***************************************************************
-***************************************************************
-***************************************************************
-***************************************************************
-***************************************************************
-***************************************************************
-***************************************************************
-***************************************************************
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
 Control character ('\x0'):   <--
-***************************************************************
+###############################################################
Index: /pyyaml/trunk/tests/data/invalid-utf8-byte.stream-error
===================================================================
--- /pyyaml/trunk/tests/data/invalid-utf8-byte.stream-error	(revision 322)
+++ /pyyaml/trunk/tests/data/invalid-utf8-byte.stream-error	(revision 333)
@@ -1,18 +1,66 @@
-***************************************************************
-***************************************************************
-***************************************************************
-***************************************************************
-***************************************************************
-***************************************************************
-***************************************************************
-***************************************************************
-***************************************************************
-***************************************************************
-***************************************************************
-***************************************************************
-***************************************************************
-***************************************************************
-***************************************************************
-***************************************************************
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
 Invalid byte ('\xFF'): ÿ <--
-***************************************************************
+###############################################################
Index: /pyyaml/trunk/tests/data/invalid-utf8-byte.loader-error
===================================================================
--- /pyyaml/trunk/tests/data/invalid-utf8-byte.loader-error	(revision 323)
+++ /pyyaml/trunk/tests/data/invalid-utf8-byte.loader-error	(revision 333)
@@ -1,18 +1,66 @@
-***************************************************************
-***************************************************************
-***************************************************************
-***************************************************************
-***************************************************************
-***************************************************************
-***************************************************************
-***************************************************************
-***************************************************************
-***************************************************************
-***************************************************************
-***************************************************************
-***************************************************************
-***************************************************************
-***************************************************************
-***************************************************************
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
 Invalid byte ('\xFF'): ÿ <--
-***************************************************************
+###############################################################
Index: /pyyaml/trunk/tests/lib/test_input_output.py
===================================================================
--- /pyyaml/trunk/tests/lib/test_input_output.py	(revision 333)
+++ /pyyaml/trunk/tests/lib/test_input_output.py	(revision 333)
@@ -0,0 +1,116 @@
+
+import yaml
+import codecs, StringIO
+
+def _unicode_open(file, encoding, errors='strict'):
+    info = codecs.lookup(encoding)
+    srw = codecs.StreamReaderWriter(file, info.streamreader, info.streamwriter, errors)
+    srw.encoding = encoding
+    return srw
+
+def test_unicode_input(unicode_filename, verbose=False):
+    data = open(unicode_filename, 'rb').read().decode('utf-8')
+    value = ' '.join(data.split())
+    output = yaml.load(_unicode_open(StringIO.StringIO(data.encode('utf-8')), 'utf-8'))
+    assert output == value, (output, value)
+    for input in [data, data.encode('utf-8'),
+                    codecs.BOM_UTF8+data.encode('utf-8'),
+                    codecs.BOM_UTF16_BE+data.encode('utf-16-be'),
+                    codecs.BOM_UTF16_LE+data.encode('utf-16-le')]:
+        if verbose:
+            print "INPUT:", repr(input[:10]), "..."
+        output = yaml.load(input)
+        assert output == value, (output, value)
+        output = yaml.load(StringIO.StringIO(input))
+        assert output == value, (output, value)
+
+test_unicode_input.unittest = ['.unicode']
+
+def test_unicode_input_errors(unicode_filename, verbose=False):
+    data = open(unicode_filename, 'rb').read().decode('utf-8')
+    for input in [data.encode('latin1', 'ignore'),
+                    data.encode('utf-16-be'), data.encode('utf-16-le'),
+                    codecs.BOM_UTF8+data.encode('utf-16-be'),
+                    codecs.BOM_UTF16_BE+data.encode('utf-16-le'),
+                    codecs.BOM_UTF16_LE+data.encode('utf-8')+'!']:
+        try:
+            yaml.load(input)
+        except yaml.YAMLError, exc:
+            if verbose:
+                print exc
+        else:
+            raise AssertionError("expected an exception")
+        try:
+            yaml.load(StringIO.StringIO(input))
+        except yaml.YAMLError, exc:
+            if verbose:
+                print exc
+        else:
+            raise AssertionError("expected an exception")
+
+test_unicode_input_errors.unittest = ['.unicode']
+
+def test_unicode_output(unicode_filename, verbose=False):
+    data = open(unicode_filename, 'rb').read().decode('utf-8')
+    value = ' '.join(data.split())
+    for encoding in [None, 'utf-8', 'utf-16-be', 'utf-16-le']:
+        for allow_unicode in [False, True]:
+            data1 = yaml.dump(value, allow_unicode=allow_unicode)
+            stream = StringIO.StringIO()
+            yaml.dump(value, _unicode_open(stream, 'utf-8'), encoding=encoding, allow_unicode=allow_unicode)
+            data2 = stream.getvalue()
+            data3 = yaml.dump(value, encoding=encoding, allow_unicode=allow_unicode)
+            stream = StringIO.StringIO()
+            yaml.dump(value, stream, encoding=encoding, allow_unicode=allow_unicode)
+            data4 = stream.getvalue()
+            for copy in [data1, data2, data3, data4]:
+                if allow_unicode:
+                    try:
+                        copy[4:].encode('ascii')
+                    except (UnicodeDecodeError, UnicodeEncodeError), exc:
+                        if verbose:
+                            print exc
+                    else:
+                        raise AssertionError("expected an exception")
+                else:
+                    copy[4:].encode('ascii')
+            assert isinstance(data1, str), (type(data1), encoding)
+            data1.decode('utf-8')
+            assert isinstance(data2, str), (type(data2), encoding)
+            data2.decode('utf-8')
+            if encoding is None:
+                assert isinstance(data3, unicode), (type(data3), encoding)
+                assert isinstance(data4, unicode), (type(data4), encoding)
+            else:
+                assert isinstance(data3, str), (type(data3), encoding)
+                data3.decode(encoding)
+                assert isinstance(data4, str), (type(data4), encoding)
+                data4.decode(encoding)
+
+test_unicode_output.unittest = ['.unicode']
+
+def test_unicode_transfer(unicode_filename, verbose=False):
+    data = open(unicode_filename, 'rb').read().decode('utf-8')
+    for encoding in [None, 'utf-8', 'utf-16-be', 'utf-16-le']:
+        input = data
+        if encoding is not None:
+            input = (u'\ufeff'+input).encode(encoding)
+        output1 = yaml.emit(yaml.parse(input), allow_unicode=True)
+        stream = StringIO.StringIO()
+        yaml.emit(yaml.parse(input), _unicode_open(stream, 'utf-8'),
+                            allow_unicode=True)
+        output2 = stream.getvalue()
+        if encoding is None:
+            assert isinstance(output1, unicode), (type(output1), encoding)
+        else:
+            assert isinstance(output1, str), (type(output1), encoding)
+            output1.decode(encoding)
+        assert isinstance(output2, str), (type(output2), encoding)
+        output2.decode('utf-8')
+
+test_unicode_transfer.unittest = ['.unicode']
+
+if __name__ == '__main__':
+    import test_appliance
+    test_appliance.run(globals())
+
Index: /pyyaml/trunk/tests/lib/test_yaml_ext.py
===================================================================
--- /pyyaml/trunk/tests/lib/test_yaml_ext.py	(revision 330)
+++ /pyyaml/trunk/tests/lib/test_yaml_ext.py	(revision 333)
@@ -268,7 +268,7 @@
 
 import test_tokens, test_structure, test_errors, test_resolver, test_constructor,   \
-        test_emitter, test_representer, test_recursive
+        test_emitter, test_representer, test_recursive, test_input_output
 wrap_ext([test_tokens, test_structure, test_errors, test_resolver, test_constructor,
-        test_emitter, test_representer, test_recursive])
+        test_emitter, test_representer, test_recursive, test_input_output])
 
 if __name__ == '__main__':
Index: /pyyaml/trunk/tests/lib/test_yaml.py
===================================================================
--- /pyyaml/trunk/tests/lib/test_yaml.py	(revision 330)
+++ /pyyaml/trunk/tests/lib/test_yaml.py	(revision 333)
@@ -11,4 +11,5 @@
 from test_representer import *
 from test_recursive import *
+from test_input_output import *
 
 if __name__ == '__main__':
