~ubuntu-branches/ubuntu/jaunty/python-docutils/jaunty

« back to all changes in this revision

Viewing changes to docutils/io.py

  • Committer: Bazaar Package Importer
  • Author(s): martin f. krafft
  • Date: 2006-07-10 11:45:05 UTC
  • mfrom: (2.1.4 edgy)
  • Revision ID: james.westby@ubuntu.com-20060710114505-otkhqcslevewxmz5
Tags: 0.4-3
Added build dependency on python-central (closes: #377580).

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
# Author: David Goodger
2
2
# Contact: goodger@users.sourceforge.net
3
 
# Revision: $Revision: 1.30 $
4
 
# Date: $Date: 2004/03/28 15:39:27 $
 
3
# Revision: $Revision: 3654 $
 
4
# Date: $Date: 2005-07-03 17:02:15 +0200 (Sun, 03 Jul 2005) $
5
5
# Copyright: This module has been placed in the public domain.
6
6
 
7
7
"""
30
30
 
31
31
    default_source_path = None
32
32
 
33
 
    def __init__(self, source=None, source_path=None, encoding=None):
 
33
    def __init__(self, source=None, source_path=None, encoding=None,
 
34
                 error_handler='strict'):
34
35
        self.encoding = encoding
35
36
        """Text encoding for the input source."""
36
37
 
 
38
        self.error_handler = error_handler
 
39
        """Text decoding error handler."""
 
40
 
37
41
        self.source = source
38
42
        """The source of input data."""
39
43
 
43
47
        if not source_path:
44
48
            self.source_path = self.default_source_path
45
49
 
 
50
        self.successful_encoding = None
 
51
        """The encoding that successfully decoded the source data."""
 
52
 
46
53
    def __repr__(self):
47
54
        return '%s: source=%r, source_path=%r' % (self.__class__, self.source,
48
55
                                                  self.source_path)
60
67
 
61
68
            locale.setlocale(locale.LC_ALL, '')
62
69
        """
63
 
        if (self.encoding and self.encoding.lower() == 'unicode'
64
 
            or isinstance(data, UnicodeType)):
65
 
            return unicode(data)
66
 
        encodings = [self.encoding, 'utf-8']
67
 
        try:
68
 
            encodings.append(locale.nl_langinfo(locale.CODESET))
69
 
        except:
70
 
            pass
71
 
        try:
72
 
            encodings.append(locale.getlocale()[1])
73
 
        except:
74
 
            pass
75
 
        try:
76
 
            encodings.append(locale.getdefaultlocale()[1])
77
 
        except:
78
 
            pass
79
 
        encodings.append('latin-1')
 
70
        if self.encoding and self.encoding.lower() == 'unicode':
 
71
            assert isinstance(data, UnicodeType), (
 
72
                'input encoding is "unicode" '
 
73
                'but input is not a unicode object')
 
74
        if isinstance(data, UnicodeType):
 
75
            # Accept unicode even if self.encoding != 'unicode'.
 
76
            return data
 
77
        encodings = [self.encoding]
 
78
        if not self.encoding:
 
79
            # Apply heuristics only if no encoding is explicitly given.
 
80
            encodings.append('utf-8')
 
81
            try:
 
82
                encodings.append(locale.nl_langinfo(locale.CODESET))
 
83
            except:
 
84
                pass
 
85
            try:
 
86
                encodings.append(locale.getlocale()[1])
 
87
            except:
 
88
                pass
 
89
            try:
 
90
                encodings.append(locale.getdefaultlocale()[1])
 
91
            except:
 
92
                pass
 
93
            encodings.append('latin-1')
 
94
        error = None
 
95
        error_details = ''
80
96
        for enc in encodings:
81
97
            if not enc:
82
98
                continue
83
99
            try:
84
 
                return unicode(data, enc)
85
 
            except (UnicodeError, LookupError):
 
100
                decoded = unicode(data, enc, self.error_handler)
 
101
                self.successful_encoding = enc
 
102
                # Return decoded, removing BOMs.
 
103
                return decoded.replace(u'\ufeff', u'')
 
104
            except (UnicodeError, LookupError), error:
86
105
                pass
 
106
        if error is not None:
 
107
            error_details = '\n(%s: %s)' % (error.__class__.__name__, error)
87
108
        raise UnicodeError(
88
 
            'Unable to decode input data.  Tried the following encodings: %s.'
89
 
            % ', '.join([repr(enc) for enc in encodings if enc]))
 
109
            'Unable to decode input data.  Tried the following encodings: '
 
110
            '%s.%s'
 
111
            % (', '.join([repr(enc) for enc in encodings if enc]),
 
112
               error_details))
90
113
 
91
114
 
92
115
class Output(TransformSpec):
121
144
                % (self.__class__, self.destination, self.destination_path))
122
145
 
123
146
    def write(self, data):
 
147
        """`data` is a Unicode string, to be encoded by `self.encode`."""
124
148
        raise NotImplementedError
125
149
 
126
150
    def encode(self, data):
127
151
        if self.encoding and self.encoding.lower() == 'unicode':
 
152
            assert isinstance(data, UnicodeType), (
 
153
                'the encoding given is "unicode" but the output is not '
 
154
                'a Unicode string')
 
155
            return data
 
156
        if not isinstance(data, UnicodeType):
 
157
            # Non-unicode (e.g. binary) output.
128
158
            return data
129
159
        else:
130
 
            return data.encode(self.encoding, self.error_handler)
 
160
            try:
 
161
                return data.encode(self.encoding, self.error_handler)
 
162
            except ValueError:
 
163
                # ValueError is raised if there are unencodable chars
 
164
                # in data and the error_handler isn't found.
 
165
                if self.error_handler == 'xmlcharrefreplace':
 
166
                    # We are using xmlcharrefreplace with a Python
 
167
                    # version that doesn't support it (2.1 or 2.2), so
 
168
                    # we emulate its behavior.
 
169
                    return ''.join([self.xmlcharref_encode(char)
 
170
                                    for char in data])
 
171
                else:
 
172
                    raise
 
173
 
 
174
    def xmlcharref_encode(self, char):
 
175
        """Emulate Python 2.3's 'xmlcharrefreplace' encoding error handler."""
 
176
        try:
 
177
            return char.encode(self.encoding, 'strict')
 
178
        except UnicodeError:
 
179
            return '&#%i;' % ord(char)
131
180
 
132
181
 
133
182
class FileInput(Input):
137
186
    """
138
187
 
139
188
    def __init__(self, source=None, source_path=None,
140
 
                 encoding=None, autoclose=1, handle_io_errors=1):
 
189
                 encoding=None, error_handler='strict',
 
190
                 autoclose=1, handle_io_errors=1):
141
191
        """
142
192
        :Parameters:
143
193
            - `source`: either a file-like object (which is read directly), or
144
194
              `None` (which implies `sys.stdin` if no `source_path` given).
145
195
            - `source_path`: a path to a file, which is opened and then read.
 
196
            - `encoding`: the expected text encoding of the input file.
 
197
            - `error_handler`: the encoding error handler to use.
146
198
            - `autoclose`: close automatically after read (boolean); always
147
199
              false if `sys.stdin` is the source.
 
200
            - `handle_io_errors`: summarize I/O errors here, and exit?
148
201
        """
149
 
        Input.__init__(self, source, source_path, encoding)
 
202
        Input.__init__(self, source, source_path, encoding, error_handler)
150
203
        self.autoclose = autoclose
151
204
        self.handle_io_errors = handle_io_errors
152
205
        if source is None:
172
225
                pass
173
226
 
174
227
    def read(self):
175
 
        """Read and decode a single file and return the data."""
176
 
        data = self.source.read()
177
 
        if self.autoclose:
178
 
            self.close()
 
228
        """
 
229
        Read and decode a single file and return the data (Unicode string).
 
230
        """
 
231
        try:
 
232
            data = self.source.read()
 
233
        finally:
 
234
            if self.autoclose:
 
235
                self.close()
179
236
        return self.decode(data)
180
237
 
181
238
    def close(self):
236
293
        output = self.encode(data)
237
294
        if not self.opened:
238
295
            self.open()
239
 
        self.destination.write(output)
240
 
        if self.autoclose:
241
 
            self.close()
 
296
        try:
 
297
            self.destination.write(output)
 
298
        finally:
 
299
            if self.autoclose:
 
300
                self.close()
242
301
        return output
243
302
 
244
303
    def close(self):
297
356
    def write(self, data):
298
357
        """Do nothing ([don't even] send data to the bit bucket)."""
299
358
        pass
 
359
 
 
360
 
 
361
class DocTreeInput(Input):
 
362
 
 
363
    """
 
364
    Adapter for document tree input.
 
365
 
 
366
    The document tree must be passed in the ``source`` parameter.
 
367
    """
 
368
 
 
369
    default_source_path = 'doctree input'
 
370
 
 
371
    def read(self):
 
372
        """Return the document tree."""
 
373
        return self.source