blob: 75f4718c20fa7dd4931cf12ad94f67de2d5ce5ff [file] [log] [blame]
Guido van Rossum4b8c6ea2000-02-04 15:39:30 +00001"""Generic output formatting.
2
3Formatter objects transform an abstract flow of formatting events into
4specific output events on writer objects. Formatters manage several stack
5structures to allow various properties of a writer object to be changed and
6restored; writers need not be able to handle relative changes nor any sort
7of ``change back'' operation. Specific writer properties which may be
8controlled via formatter objects are horizontal alignment, font, and left
9margin indentations. A mechanism is provided which supports providing
10arbitrary, non-exclusive style settings to a writer as well. Additional
11interfaces facilitate formatting events which are not reversible, such as
Tim Peters88869f92001-01-14 23:36:06 +000012paragraph separation.
Guido van Rossum4b8c6ea2000-02-04 15:39:30 +000013
14Writer objects encapsulate device interfaces. Abstract devices, such as
15file formats, are supported as well as physical devices. The provided
16implementations all work with abstract devices. The interface makes
17available mechanisms for setting the properties which formatter objects
Tim Peters88869f92001-01-14 23:36:06 +000018manage and inserting data into the output.
Guido van Rossum4b8c6ea2000-02-04 15:39:30 +000019"""
20
Guido van Rossuma0eab1d1995-08-07 20:07:36 +000021import string
22import sys
Guido van Rossum9787bdaf1996-05-28 23:50:49 +000023from types import StringType
Guido van Rossuma0eab1d1995-08-07 20:07:36 +000024
25
26AS_IS = None
27
Guido van Rossuma0eab1d1995-08-07 20:07:36 +000028
Guido van Rossum909507d1995-10-06 15:31:30 +000029class NullFormatter:
Raymond Hettingeraef22fb2002-05-29 16:18:42 +000030 """A formatter which does nothing.
31
32 If the writer parameter is omitted, a NullWriter instance is created.
33 No methods of the writer are called by NullFormatter instances.
34
35 Implementations should inherit from this class if implementing a writer
36 interface but don't need to inherit any implementation.
37
38 """
Guido van Rossum909507d1995-10-06 15:31:30 +000039
Guido van Rossumccd8b191996-10-07 21:29:49 +000040 def __init__(self, writer=None):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000041 if not writer:
42 writer = NullWriter()
43 self.writer = writer
Guido van Rossum909507d1995-10-06 15:31:30 +000044 def end_paragraph(self, blankline): pass
45 def add_line_break(self): pass
Fred Drakeca8cdc61996-10-08 21:57:47 +000046 def add_hor_rule(self, *args, **kw): pass
47 def add_label_data(self, format, counter, blankline=None): pass
Guido van Rossum909507d1995-10-06 15:31:30 +000048 def add_flowing_data(self, data): pass
49 def add_literal_data(self, data): pass
50 def flush_softspace(self): pass
Guido van Rossum9787bdaf1996-05-28 23:50:49 +000051 def push_alignment(self, align): pass
52 def pop_alignment(self): pass
Guido van Rossum909507d1995-10-06 15:31:30 +000053 def push_font(self, x): pass
54 def pop_font(self): pass
55 def push_margin(self, margin): pass
56 def pop_margin(self): pass
57 def set_spacing(self, spacing): pass
Guido van Rossum9787bdaf1996-05-28 23:50:49 +000058 def push_style(self, *styles): pass
59 def pop_style(self, n=1): pass
60 def assert_line_data(self, flag=1): pass
Guido van Rossum909507d1995-10-06 15:31:30 +000061
62
Guido van Rossuma0eab1d1995-08-07 20:07:36 +000063class AbstractFormatter:
Raymond Hettingeraef22fb2002-05-29 16:18:42 +000064 """The standard formatter.
65
66 This implementation has demonstrated wide applicability to many writers,
67 and may be used directly in most circumstances. It has been used to
68 implement a full-featured World Wide Web browser.
69
70 """
Guido van Rossuma0eab1d1995-08-07 20:07:36 +000071
Guido van Rossum8e449911996-08-26 16:19:23 +000072 # Space handling policy: blank spaces at the boundary between elements
73 # are handled by the outermost context. "Literal" data is not checked
74 # to determine context, so spaces in literal data are handled directly
75 # in all circumstances.
76
Guido van Rossuma0eab1d1995-08-07 20:07:36 +000077 def __init__(self, writer):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000078 self.writer = writer # Output device
79 self.align = None # Current alignment
80 self.align_stack = [] # Alignment stack
81 self.font_stack = [] # Font state
82 self.margin_stack = [] # Margin state
83 self.spacing = None # Vertical spacing state
84 self.style_stack = [] # Other state, e.g. color
85 self.nospace = 1 # Should leading space be suppressed
86 self.softspace = 0 # Should a space be inserted
87 self.para_end = 1 # Just ended a paragraph
88 self.parskip = 0 # Skipped space between paragraphs?
89 self.hard_break = 1 # Have a hard break
90 self.have_label = 0
Guido van Rossuma0eab1d1995-08-07 20:07:36 +000091
92 def end_paragraph(self, blankline):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000093 if not self.hard_break:
94 self.writer.send_line_break()
95 self.have_label = 0
96 if self.parskip < blankline and not self.have_label:
97 self.writer.send_paragraph(blankline - self.parskip)
98 self.parskip = blankline
99 self.have_label = 0
100 self.hard_break = self.nospace = self.para_end = 1
101 self.softspace = 0
Guido van Rossuma0eab1d1995-08-07 20:07:36 +0000102
103 def add_line_break(self):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000104 if not (self.hard_break or self.para_end):
105 self.writer.send_line_break()
106 self.have_label = self.parskip = 0
107 self.hard_break = self.nospace = 1
108 self.softspace = 0
Guido van Rossuma0eab1d1995-08-07 20:07:36 +0000109
Guido van Rossum9787bdaf1996-05-28 23:50:49 +0000110 def add_hor_rule(self, *args, **kw):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000111 if not self.hard_break:
112 self.writer.send_line_break()
113 apply(self.writer.send_hor_rule, args, kw)
114 self.hard_break = self.nospace = 1
115 self.have_label = self.para_end = self.softspace = self.parskip = 0
Guido van Rossuma0eab1d1995-08-07 20:07:36 +0000116
Guido van Rossum9787bdaf1996-05-28 23:50:49 +0000117 def add_label_data(self, format, counter, blankline = None):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000118 if self.have_label or not self.hard_break:
119 self.writer.send_line_break()
120 if not self.para_end:
121 self.writer.send_paragraph((blankline and 1) or 0)
122 if type(format) is StringType:
123 self.writer.send_label_data(self.format_counter(format, counter))
124 else:
125 self.writer.send_label_data(format)
126 self.nospace = self.have_label = self.hard_break = self.para_end = 1
127 self.softspace = self.parskip = 0
Guido van Rossuma0eab1d1995-08-07 20:07:36 +0000128
129 def format_counter(self, format, counter):
Guido van Rossuma0eab1d1995-08-07 20:07:36 +0000130 label = ''
131 for c in format:
Fred Drake6f6a14f2001-05-11 19:25:08 +0000132 if c == '1':
133 label = label + ('%d' % counter)
134 elif c in 'aA':
135 if counter > 0:
136 label = label + self.format_letter(c, counter)
137 elif c in 'iI':
138 if counter > 0:
139 label = label + self.format_roman(c, counter)
140 else:
Guido van Rossum9787bdaf1996-05-28 23:50:49 +0000141 label = label + c
Guido van Rossuma0eab1d1995-08-07 20:07:36 +0000142 return label
143
144 def format_letter(self, case, counter):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000145 label = ''
146 while counter > 0:
147 counter, x = divmod(counter-1, 26)
Fred Drake6f6a14f2001-05-11 19:25:08 +0000148 # This makes a strong assumption that lowercase letters
149 # and uppercase letters form two contiguous blocks, with
150 # letters in order!
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000151 s = chr(ord(case) + x)
152 label = s + label
153 return label
Guido van Rossuma0eab1d1995-08-07 20:07:36 +0000154
155 def format_roman(self, case, counter):
156 ones = ['i', 'x', 'c', 'm']
157 fives = ['v', 'l', 'd']
Guido van Rossum9787bdaf1996-05-28 23:50:49 +0000158 label, index = '', 0
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000159 # This will die of IndexError when counter is too big
Guido van Rossuma0eab1d1995-08-07 20:07:36 +0000160 while counter > 0:
161 counter, x = divmod(counter, 10)
162 if x == 9:
Guido van Rossum9787bdaf1996-05-28 23:50:49 +0000163 label = ones[index] + ones[index+1] + label
Guido van Rossuma0eab1d1995-08-07 20:07:36 +0000164 elif x == 4:
Guido van Rossum9787bdaf1996-05-28 23:50:49 +0000165 label = ones[index] + fives[index] + label
Guido van Rossuma0eab1d1995-08-07 20:07:36 +0000166 else:
167 if x >= 5:
168 s = fives[index]
169 x = x-5
170 else:
171 s = ''
172 s = s + ones[index]*x
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000173 label = s + label
Guido van Rossuma0eab1d1995-08-07 20:07:36 +0000174 index = index + 1
Guido van Rossum9787bdaf1996-05-28 23:50:49 +0000175 if case == 'I':
Eric S. Raymondb08b2d32001-02-09 11:10:16 +0000176 return label.upper()
Guido van Rossuma0eab1d1995-08-07 20:07:36 +0000177 return label
178
Guido van Rossum9787bdaf1996-05-28 23:50:49 +0000179 def add_flowing_data(self, data,
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000180 # These are only here to load them into locals:
181 whitespace = string.whitespace,
182 join = string.join, split = string.split):
183 if not data: return
184 # The following looks a bit convoluted but is a great improvement over
185 # data = regsub.gsub('[' + string.whitespace + ']+', ' ', data)
186 prespace = data[:1] in whitespace
187 postspace = data[-1:] in whitespace
188 data = join(split(data))
189 if self.nospace and not data:
190 return
191 elif prespace or self.softspace:
192 if not data:
193 if not self.nospace:
194 self.softspace = 1
195 self.parskip = 0
196 return
197 if not self.nospace:
198 data = ' ' + data
199 self.hard_break = self.nospace = self.para_end = \
200 self.parskip = self.have_label = 0
201 self.softspace = postspace
202 self.writer.send_flowing_data(data)
Guido van Rossuma0eab1d1995-08-07 20:07:36 +0000203
204 def add_literal_data(self, data):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000205 if not data: return
206 if self.softspace:
207 self.writer.send_flowing_data(" ")
208 self.hard_break = data[-1:] == '\n'
209 self.nospace = self.para_end = self.softspace = \
210 self.parskip = self.have_label = 0
211 self.writer.send_literal_data(data)
Guido van Rossuma0eab1d1995-08-07 20:07:36 +0000212
Guido van Rossumc7ae9201995-09-30 16:49:58 +0000213 def flush_softspace(self):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000214 if self.softspace:
215 self.hard_break = self.para_end = self.parskip = \
216 self.have_label = self.softspace = 0
217 self.nospace = 1
218 self.writer.send_flowing_data(' ')
Guido van Rossumc7ae9201995-09-30 16:49:58 +0000219
Guido van Rossum9787bdaf1996-05-28 23:50:49 +0000220 def push_alignment(self, align):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000221 if align and align != self.align:
222 self.writer.new_alignment(align)
223 self.align = align
224 self.align_stack.append(align)
225 else:
226 self.align_stack.append(self.align)
Guido van Rossum9787bdaf1996-05-28 23:50:49 +0000227
228 def pop_alignment(self):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000229 if self.align_stack:
230 del self.align_stack[-1]
231 if self.align_stack:
232 self.align = align = self.align_stack[-1]
233 self.writer.new_alignment(align)
234 else:
235 self.align = None
236 self.writer.new_alignment(None)
Guido van Rossum9787bdaf1996-05-28 23:50:49 +0000237
Guido van Rossuma0eab1d1995-08-07 20:07:36 +0000238 def push_font(self, (size, i, b, tt)):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000239 if self.softspace:
240 self.hard_break = self.para_end = self.softspace = 0
241 self.nospace = 1
242 self.writer.send_flowing_data(' ')
243 if self.font_stack:
244 csize, ci, cb, ctt = self.font_stack[-1]
245 if size is AS_IS: size = csize
246 if i is AS_IS: i = ci
247 if b is AS_IS: b = cb
248 if tt is AS_IS: tt = ctt
249 font = (size, i, b, tt)
250 self.font_stack.append(font)
251 self.writer.new_font(font)
Guido van Rossuma0eab1d1995-08-07 20:07:36 +0000252
253 def pop_font(self):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000254 if self.font_stack:
255 del self.font_stack[-1]
256 if self.font_stack:
257 font = self.font_stack[-1]
258 else:
259 font = None
260 self.writer.new_font(font)
Guido van Rossuma0eab1d1995-08-07 20:07:36 +0000261
262 def push_margin(self, margin):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000263 self.margin_stack.append(margin)
264 fstack = filter(None, self.margin_stack)
265 if not margin and fstack:
266 margin = fstack[-1]
267 self.writer.new_margin(margin, len(fstack))
Guido van Rossuma0eab1d1995-08-07 20:07:36 +0000268
269 def pop_margin(self):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000270 if self.margin_stack:
271 del self.margin_stack[-1]
272 fstack = filter(None, self.margin_stack)
273 if fstack:
274 margin = fstack[-1]
275 else:
276 margin = None
277 self.writer.new_margin(margin, len(fstack))
Guido van Rossuma0eab1d1995-08-07 20:07:36 +0000278
279 def set_spacing(self, spacing):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000280 self.spacing = spacing
281 self.writer.new_spacing(spacing)
Guido van Rossuma0eab1d1995-08-07 20:07:36 +0000282
Guido van Rossum9787bdaf1996-05-28 23:50:49 +0000283 def push_style(self, *styles):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000284 if self.softspace:
285 self.hard_break = self.para_end = self.softspace = 0
286 self.nospace = 1
287 self.writer.send_flowing_data(' ')
288 for style in styles:
289 self.style_stack.append(style)
290 self.writer.new_styles(tuple(self.style_stack))
Guido van Rossuma0eab1d1995-08-07 20:07:36 +0000291
Guido van Rossum9787bdaf1996-05-28 23:50:49 +0000292 def pop_style(self, n=1):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000293 del self.style_stack[-n:]
294 self.writer.new_styles(tuple(self.style_stack))
Guido van Rossuma0eab1d1995-08-07 20:07:36 +0000295
Guido van Rossum9787bdaf1996-05-28 23:50:49 +0000296 def assert_line_data(self, flag=1):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000297 self.nospace = self.hard_break = not flag
298 self.para_end = self.parskip = self.have_label = 0
Guido van Rossum9787bdaf1996-05-28 23:50:49 +0000299
300
301class NullWriter:
Raymond Hettingeraef22fb2002-05-29 16:18:42 +0000302 """Minimal writer interface to use in testing & inheritance.
303
304 A writer which only provides the interface definition; no actions are
305 taken on any methods. This should be the base class for all writers
306 which do not need to inherit any implementation methods.
307
308 """
Guido van Rossum3672aa21996-05-29 00:02:30 +0000309 def __init__(self): pass
Fred Drake28231681996-12-31 20:50:51 +0000310 def flush(self): pass
Guido van Rossum9787bdaf1996-05-28 23:50:49 +0000311 def new_alignment(self, align): pass
312 def new_font(self, font): pass
313 def new_margin(self, margin, level): pass
314 def new_spacing(self, spacing): pass
315 def new_styles(self, styles): pass
316 def send_paragraph(self, blankline): pass
317 def send_line_break(self): pass
318 def send_hor_rule(self, *args, **kw): pass
319 def send_label_data(self, data): pass
320 def send_flowing_data(self, data): pass
321 def send_literal_data(self, data): pass
322
Guido van Rossuma0eab1d1995-08-07 20:07:36 +0000323
Guido van Rossum3672aa21996-05-29 00:02:30 +0000324class AbstractWriter(NullWriter):
Raymond Hettingeraef22fb2002-05-29 16:18:42 +0000325 """A writer which can be used in debugging formatters, but not much else.
326
327 Each method simply announces itself by printing its name and
328 arguments on standard output.
329
330 """
Guido van Rossuma0eab1d1995-08-07 20:07:36 +0000331
Guido van Rossum9787bdaf1996-05-28 23:50:49 +0000332 def new_alignment(self, align):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000333 print "new_alignment(%s)" % `align`
Guido van Rossum9787bdaf1996-05-28 23:50:49 +0000334
Guido van Rossuma0eab1d1995-08-07 20:07:36 +0000335 def new_font(self, font):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000336 print "new_font(%s)" % `font`
Guido van Rossuma0eab1d1995-08-07 20:07:36 +0000337
338 def new_margin(self, margin, level):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000339 print "new_margin(%s, %d)" % (`margin`, level)
Guido van Rossuma0eab1d1995-08-07 20:07:36 +0000340
341 def new_spacing(self, spacing):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000342 print "new_spacing(%s)" % `spacing`
Guido van Rossuma0eab1d1995-08-07 20:07:36 +0000343
344 def new_styles(self, styles):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000345 print "new_styles(%s)" % `styles`
Guido van Rossuma0eab1d1995-08-07 20:07:36 +0000346
347 def send_paragraph(self, blankline):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000348 print "send_paragraph(%s)" % `blankline`
Guido van Rossuma0eab1d1995-08-07 20:07:36 +0000349
350 def send_line_break(self):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000351 print "send_line_break()"
Guido van Rossuma0eab1d1995-08-07 20:07:36 +0000352
Guido van Rossum9787bdaf1996-05-28 23:50:49 +0000353 def send_hor_rule(self, *args, **kw):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000354 print "send_hor_rule()"
Guido van Rossuma0eab1d1995-08-07 20:07:36 +0000355
356 def send_label_data(self, data):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000357 print "send_label_data(%s)" % `data`
Guido van Rossuma0eab1d1995-08-07 20:07:36 +0000358
359 def send_flowing_data(self, data):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000360 print "send_flowing_data(%s)" % `data`
Guido van Rossuma0eab1d1995-08-07 20:07:36 +0000361
362 def send_literal_data(self, data):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000363 print "send_literal_data(%s)" % `data`
Guido van Rossuma0eab1d1995-08-07 20:07:36 +0000364
365
Guido van Rossum3672aa21996-05-29 00:02:30 +0000366class DumbWriter(NullWriter):
Raymond Hettingeraef22fb2002-05-29 16:18:42 +0000367 """Simple writer class which writes output on the file object passed in
368 as the file parameter or, if file is omitted, on standard output. The
369 output is simply word-wrapped to the number of columns specified by
370 the maxcol parameter. This class is suitable for reflowing a sequence
371 of paragraphs.
372
373 """
Guido van Rossuma0eab1d1995-08-07 20:07:36 +0000374
375 def __init__(self, file=None, maxcol=72):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000376 self.file = file or sys.stdout
377 self.maxcol = maxcol
378 NullWriter.__init__(self)
379 self.reset()
Guido van Rossuma0eab1d1995-08-07 20:07:36 +0000380
381 def reset(self):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000382 self.col = 0
383 self.atbreak = 0
Guido van Rossuma0eab1d1995-08-07 20:07:36 +0000384
385 def send_paragraph(self, blankline):
Fred Drakef4bb6561999-01-12 18:13:27 +0000386 self.file.write('\n'*blankline)
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000387 self.col = 0
388 self.atbreak = 0
Guido van Rossuma0eab1d1995-08-07 20:07:36 +0000389
390 def send_line_break(self):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000391 self.file.write('\n')
392 self.col = 0
393 self.atbreak = 0
Guido van Rossuma0eab1d1995-08-07 20:07:36 +0000394
Guido van Rossum9787bdaf1996-05-28 23:50:49 +0000395 def send_hor_rule(self, *args, **kw):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000396 self.file.write('\n')
397 self.file.write('-'*self.maxcol)
398 self.file.write('\n')
399 self.col = 0
400 self.atbreak = 0
Guido van Rossuma0eab1d1995-08-07 20:07:36 +0000401
402 def send_literal_data(self, data):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000403 self.file.write(data)
Eric S. Raymondb08b2d32001-02-09 11:10:16 +0000404 i = data.rfind('\n')
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000405 if i >= 0:
406 self.col = 0
407 data = data[i+1:]
Eric S. Raymondb08b2d32001-02-09 11:10:16 +0000408 data = data.expandtabs()
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000409 self.col = self.col + len(data)
410 self.atbreak = 0
Guido van Rossuma0eab1d1995-08-07 20:07:36 +0000411
412 def send_flowing_data(self, data):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000413 if not data: return
414 atbreak = self.atbreak or data[0] in string.whitespace
415 col = self.col
416 maxcol = self.maxcol
417 write = self.file.write
Eric S. Raymondb08b2d32001-02-09 11:10:16 +0000418 for word in data.split():
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000419 if atbreak:
420 if col + len(word) >= maxcol:
421 write('\n')
422 col = 0
423 else:
424 write(' ')
425 col = col + 1
426 write(word)
427 col = col + len(word)
428 atbreak = 1
429 self.col = col
430 self.atbreak = data[-1] in string.whitespace
Guido van Rossuma0eab1d1995-08-07 20:07:36 +0000431
432
433def test(file = None):
434 w = DumbWriter()
435 f = AbstractFormatter(w)
436 if file:
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000437 fp = open(file)
Guido van Rossuma0eab1d1995-08-07 20:07:36 +0000438 elif sys.argv[1:]:
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000439 fp = open(sys.argv[1])
Guido van Rossuma0eab1d1995-08-07 20:07:36 +0000440 else:
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000441 fp = sys.stdin
Guido van Rossuma0eab1d1995-08-07 20:07:36 +0000442 while 1:
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000443 line = fp.readline()
444 if not line:
445 break
446 if line == '\n':
447 f.end_paragraph(1)
448 else:
449 f.add_flowing_data(line)
Guido van Rossuma0eab1d1995-08-07 20:07:36 +0000450 f.end_paragraph(0)
451
452
453if __name__ == '__main__':
454 test()