# HG changeset patch # User Paul Boddie # Date 1225669686 -3600 # Node ID 18ef1a1eab60deb127abdab2d2eab157560493d2 # Parent c408f51100a9c18e2ba13fdb0ab6f7189c2dbdfb Renamed the StreamWriter.write method to write_content_line. Added support for skipping blank lines when reading content. Added support for opening streams using filenames in the convenience methods, introducing close methods on certain classes in order to support the proper closure of streams after use. diff -r c408f51100a9 -r 18ef1a1eab60 tests/test_calendar_stream.py --- a/tests/test_calendar_stream.py Sun Nov 02 23:06:25 2008 +0100 +++ b/tests/test_calendar_stream.py Mon Nov 03 00:48:06 2008 +0100 @@ -9,7 +9,7 @@ w = vCalendar.iterwrite(out) for name, parameters, value in doc: print "%r, %r, %r" % (name, parameters, value) - w.write(name, parameters, value) + w.write_content_line(name, parameters, value) finally: out.close() f.close() diff -r c408f51100a9 -r 18ef1a1eab60 tests/test_card_stream.py --- a/tests/test_card_stream.py Sun Nov 02 23:06:25 2008 +0100 +++ b/tests/test_card_stream.py Mon Nov 03 00:48:06 2008 +0100 @@ -9,7 +9,7 @@ w = vContent.iterwrite(out) for name, parameters, value in doc: print "%r, %r, %r" % (name, parameters, value) - w.write(name, parameters, value) + w.write_content_line(name, parameters, value) finally: out.close() f.close() diff -r c408f51100a9 -r 18ef1a1eab60 vCalendar.py --- a/vCalendar.py Sun Nov 02 23:06:25 2008 +0100 +++ b/vCalendar.py Mon Nov 03 00:48:06 2008 +0100 @@ -161,12 +161,13 @@ # Public functions. -def parse(f, non_standard_newline=0): +def parse(stream_or_string, non_standard_newline=0): """ - Parse the resource data found through the use of the file object 'f', which - should provide Unicode data. (The codecs module can be used to open files or - to wrap streams in order to provide Unicode data.) + Parse the resource data found through the use of the 'stream_or_string', + which is either a stream providing Unicode data (the codecs module can be + used to open files or to wrap streams in order to provide Unicode data) or a + filename identifying a file to be parsed. The optional 'non_standard_newline' can be set to a true value (unlike the default) in order to attempt to process files with CR as the end of line @@ -176,14 +177,15 @@ is returned. """ - return vContent.parse(f, non_standard_newline, vCalendarParser) + return vContent.parse(stream_or_string, non_standard_newline, vCalendarParser) -def iterparse(f, non_standard_newline=0): +def iterparse(stream_or_string, non_standard_newline=0): """ - Parse the resource data found through the use of the file object 'f', which - should provide Unicode data. (The codecs module can be used to open files or - to wrap streams in order to provide Unicode data.) + Parse the resource data found through the use of the 'stream_or_string', + which is either a stream providing Unicode data (the codecs module can be + used to open files or to wrap streams in order to provide Unicode data) or a + filename identifying a file to be parsed. The optional 'non_standard_newline' can be set to a true value (unlike the default) in order to attempt to process files with CR as the end of line @@ -193,9 +195,9 @@ events of the form (name, parameters, value). """ - return vContent.iterparse(f, non_standard_newline, vCalendarStreamParser) + return vContent.iterparse(stream_or_string, non_standard_newline, vCalendarStreamParser) -def iterwrite(f, line_length=None): - return vContent.iterwrite(f, line_length, vCalendarStreamWriter) +def iterwrite(stream_or_string, line_length=None): + return vContent.iterwrite(stream_or_string, line_length, vCalendarStreamWriter) # vim: tabstop=4 expandtab shiftwidth=4 diff -r c408f51100a9 -r 18ef1a1eab60 vContent.py --- a/vContent.py Sun Nov 02 23:06:25 2008 +0100 +++ b/vContent.py Mon Nov 03 00:48:06 2008 +0100 @@ -41,11 +41,16 @@ # Encoding-related imports. import base64, quopri +import codecs # Tokenisation help. import re +# Configuration. + +default_encoding = "utf-8" + # Reader and parser classes. class Reader: @@ -65,6 +70,12 @@ self.lines = [] self.line_number = 1 # about to read line 1 + def close(self): + + "Close the reader." + + self.f.close() + def pushback(self, line): """ @@ -102,13 +113,23 @@ physical lines of text. """ + # Skip blank lines. + line = self.readline() + while line: + line_stripped = line.rstrip("\r\n") + if not line_stripped: + line = self.readline() + else: + break + else: + return "" # Strip all appropriate whitespace from the right end of each line. # For subsequent lines, remove the first whitespace character. # See section 4.1 of the iCalendar specification. - lines = [line.rstrip("\r\n")] + lines = [line_stripped] line = self.readline() while line.startswith(" ") or line.startswith("\t"): @@ -212,6 +233,12 @@ self.f = f + def close(self): + + "Close the reader." + + self.f.close() + def __iter__(self): "Return self as the iterator." @@ -424,6 +451,12 @@ self.line_length = line_length or self.default_line_length self.char_offset = 0 + def close(self): + + "Close the writer." + + self.f.close() + def write(self, text): "Write the 'text' to the file." @@ -466,7 +499,13 @@ self.f = f - def write(self, name, parameters, value): + def close(self): + + "Close the writer." + + self.f.close() + + def write_content_line(self, name, parameters, value): """ Write a content line for the given 'name', 'parameters' and 'value' @@ -507,14 +546,32 @@ return self.encode_content(value) +# Utility functions. + +def is_input_stream(stream_or_string): + return hasattr(stream_or_string, "read") + +def get_input_stream(stream_or_string): + if is_input_stream(stream_or_string): + return stream_or_string + else: + return codecs.open(stream_or_string, encoding=default_encoding) + +def get_output_stream(stream_or_string): + if hasattr(stream_or_string, "write"): + return stream_or_string + else: + return codecs.open(stream_or_string, "w", encoding=default_encoding) + # Public functions. -def parse(f, non_standard_newline=0, parser_cls=None): +def parse(stream_or_string, non_standard_newline=0, parser_cls=None): """ - Parse the resource data found through the use of the file object 'f', which - should provide Unicode data. (The codecs module can be used to open files or - to wrap streams in order to provide Unicode data.) + Parse the resource data found through the use of the 'stream_or_string', + which is either a stream providing Unicode data (the codecs module can be + used to open files or to wrap streams in order to provide Unicode data) or a + filename identifying a file to be parsed. The optional 'non_standard_newline' can be set to a true value (unlike the default) in order to attempt to process files with CR as the end of line @@ -524,16 +581,28 @@ is returned. """ - reader = Reader(f, non_standard_newline) - parser = (parser_cls or Parser)() - return parser.parse(reader) + stream = get_input_stream(stream_or_string) + reader = Reader(stream, non_standard_newline) + + # Parse using the reader. -def iterparse(f, non_standard_newline=0, parser_cls=None): + try: + parser = (parser_cls or Parser)() + return parser.parse(reader) + + # Close any opened streams. + + finally: + if not is_input_stream(stream_or_string): + reader.close() + +def iterparse(stream_or_string, non_standard_newline=0, parser_cls=None): """ - Parse the resource data found through the use of the file object 'f', which - should provide Unicode data. (The codecs module can be used to open files or - to wrap streams in order to provide Unicode data.) + Parse the resource data found through the use of the 'stream_or_string', + which is either a stream providing Unicode data (the codecs module can be + used to open files or to wrap streams in order to provide Unicode data) or a + filename identifying a file to be parsed. The optional 'non_standard_newline' can be set to a true value (unlike the default) in order to attempt to process files with CR as the end of line @@ -543,12 +612,14 @@ events of the form (name, parameters, value). """ - reader = Reader(f, non_standard_newline) + stream = get_input_stream(stream_or_string) + reader = Reader(stream, non_standard_newline) parser = (parser_cls or StreamParser)(reader) - return iter(parser) + return parser -def iterwrite(f, line_length=None, writer_cls=None): - _writer = Writer(f, line_length) +def iterwrite(stream_or_string, line_length=None, writer_cls=None): + stream = get_output_stream(stream_or_string) + _writer = Writer(stream, line_length) writer = (writer_cls or StreamWriter)(_writer) return writer