diff options
author | milde <milde@929543f6-e4f2-0310-98a6-ba3bd3dd1d04> | 2012-06-13 14:14:12 +0000 |
---|---|---|
committer | milde <milde@929543f6-e4f2-0310-98a6-ba3bd3dd1d04> | 2012-06-13 14:14:12 +0000 |
commit | 6ecaeec7db2158e5692373a1be38d6820367e221 (patch) | |
tree | 89d9ab25474dfbf9d1ba033e6b02e5543eb64f20 /docutils/io.py | |
parent | d4b8bb1e834f812600c35ac82950a420f936ca00 (diff) | |
download | docutils-6ecaeec7db2158e5692373a1be38d6820367e221.tar.gz |
Fixup: more save implementation of binary data output under Python 3.
Prevent test error under Python 3. Add tests for FileOutput. Document.
git-svn-id: http://svn.code.sf.net/p/docutils/code/trunk/docutils@7440 929543f6-e4f2-0310-98a6-ba3bd3dd1d04
Diffstat (limited to 'docutils/io.py')
-rw-r--r-- | docutils/io.py | 76 |
1 files changed, 44 insertions, 32 deletions
diff --git a/docutils/io.py b/docutils/io.py index f44ed6e93..b992e6d71 100644 --- a/docutils/io.py +++ b/docutils/io.py @@ -21,6 +21,21 @@ from docutils.error_reporting import locale_encoding, ErrorString, ErrorOutput class InputError(IOError): pass class OutputError(IOError): pass +def check_encoding(stream, encoding): + """Test, whether the encoding of `stream` matches `encoding`. + + Returns + + :None: if `encoding` or `stream.encoding` are not a valid encoding + argument (e.g. ``None``) or `stream.encoding is missing. + :True: if the encoding argument resolves to the same value as `encoding`, + :False: if the encodings differ. + """ + try: + return codecs.lookup(stream.encoding) == codecs.lookup(encoding) + except (LookupError, AttributeError, TypeError): + return None + class Input(TransformSpec): @@ -231,10 +246,7 @@ class FileInput(Input): else: self.source = sys.stdin elif (sys.version_info >= (3,0) and - self.encoding and hasattr(self.source, 'encoding') and - self.encoding != self.source.encoding and - codecs.lookup(self.encoding) != - codecs.lookup(self.source.encoding)): + check_encoding(self.source, self.encoding) is False): # TODO: re-open, warn or raise error? raise UnicodeError('Encoding clash: encoding given is "%s" ' 'but source is opened with encoding "%s".' % @@ -327,10 +339,7 @@ class FileOutput(Output): if destination_path: self.opened = False else: - if sys.version_info >= (3,0) and 'b' in self.mode: - self.destination = sys.stdout.buffer - else: - self.destination = sys.stdout + self.destination = sys.stdout elif (# destination is file-type object -> check mode: mode and hasattr(self.destination, 'mode') and mode != self.destination.mode): @@ -342,16 +351,21 @@ class FileOutput(Output): self.destination_path = self.destination.name except AttributeError: pass - if (encoding and hasattr(self.destination, 'encoding') - and codecs.lookup(self.encoding) != - codecs.lookup(self.destination.encoding)): - if self.destination is sys.stdout and sys.version_info >= (3,0): - self.destination = sys.stdout.buffer - else: - raise UnicodeError('Encoding of %s (%s) ' - 'differs from specified encoding (%s)' % - (self.destination_path or 'destination', - self.destination.encoding, encoding)) + # Special cases under Python 3: different encoding or binary output + if sys.version_info >= (3,0): + if ('b' in self.mode + and self.destination in (sys.stdout, sys.stderr) + ): + self.destination = self.destination.buffer + if check_encoding(self.destination, self.encoding) is False: + if self.destination in (sys.stdout, sys.stderr): + self.destination = self.destination.buffer + else: # TODO: try the `write to .buffer` scheme instead? + raise ValueError('Encoding of %s (%s) differs \n' + ' from specified encoding (%s)' % + (self.destination_path or 'destination', + destination.encoding, encoding)) + def open(self): # Specify encoding in Python 3. @@ -375,25 +389,23 @@ class FileOutput(Output): def write(self, data): """Encode `data`, write it to a single file, and return it. - With Python 3 or binary output mode, `data` is returned unchanged. + With Python 3 or binary output mode, `data` is returned unchanged, + except when specified encoding and output encoding differ. """ - if sys.version_info < (3,0) and 'b' not in self.mode: - data = self.encode(data) if not self.opened: self.open() try: # In Python < 2.5, try...except has to be nested in try...finally. try: - if (sys.version_info >= (3,0) - and self.destination is sys.stdout.buffer - and 'b' not in self.mode): - # encode now, as sys.stdout.encoding != self.encoding - bdata = self.encode(data) - if os.linesep != '\n': - bdata = bdata.replace('\n', os.linesep) - self.destination.buffer.write(bdata) - else: - self.destination.write(data) - except (UnicodeError, LookupError), err: # can only happen in py3k + if 'b' not in self.mode and (sys.version_info < (3,0) or + check_encoding(self.destination, self.encoding) is False): + data = self.encode(data) + if sys.version_info >= (3,0) and os.linesep != '\n': + # writing as binary data -> fix endings + data = data.replace('\n', os.linesep) + + self.destination.write(data) + + except (UnicodeError, LookupError), err: raise UnicodeError( 'Unable to encode output data. output-encoding is: ' '%s.\n(%s)' % (self.encoding, ErrorString(err))) |