vContent

vCalendar.py

8:c408f51100a9
2008-11-02 Paul Boddie Overhauled the reading and writing to more properly handle folded lines, introducing a ContentLine class for parsing whole content lines, and making a separate Writer class which is able to transparently fold lines for the StreamWriter class. Added iterwrite functions, although their name could be better chosen. Updated the tests to more properly test reading and to test writing.
     1 #!/usr/bin/env python     2      3 """     4 Parsing of vCalendar and iCalendar files.     5      6 Copyright (C) 2008 Paul Boddie <paul@boddie.org.uk>     7      8 This program is free software; you can redistribute it and/or modify it under     9 the terms of the GNU Lesser General Public License as published by the Free    10 Software Foundation; either version 3 of the License, or (at your option) any    11 later version.    12     13 This program is distributed in the hope that it will be useful, but WITHOUT    14 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS    15 FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more    16 details.    17     18 You should have received a copy of the GNU Lesser General Public License along    19 with this program.  If not, see <http://www.gnu.org/licenses/>.    20     21 --------    22     23 References:    24     25 RFC 2445: Internet Calendaring and Scheduling Core Object Specification    26           (iCalendar)    27           http://rfc.net/rfc2445.html    28 """    29     30 import vContent    31     32 try:    33     set    34 except NameError:    35     from sets import Set as set    36     37 # Format details.    38     39 QUOTED_PARAMETERS = set([    40     "ALTREP", "DELEGATED-FROM", "DELEGATED-TO", "DIR", "MEMBER", "SENT-BY"    41     ])    42 MULTIVALUED_PARAMETERS = set([    43     "DELEGATED-FROM", "DELEGATED-TO", "MEMBER"    44     ])    45 QUOTED_TYPES = set(["URI"])    46     47 # Parser classes.    48     49 class vCalendarStreamParser(vContent.StreamParser):    50     51     "A stream parser specifically for vCalendar/iCalendar."    52     53     def next(self):    54     55         """    56         Return the next content item in the file as a tuple of the form    57         (name, parameters, value).    58         """    59     60         name, parameters, value = vContent.StreamParser.next(self)    61         return name, self.decode_parameters(parameters), self.decode_content(value)    62     63     def decode_content(self, value):    64     65         "Decode the given 'value', replacing quoted separator characters."    66     67         # Replace quoted characters (see 4.3.11 in RFC 2445).    68     69         value = vContent.StreamParser.decode_content(self, value)    70         return value.replace("\\,", ",").replace("\\;", ";")    71     72     # Internal methods.    73     74     def decode_quoted_value(self, value):    75     76         "Decode the given 'value', returning a list of decoded values."    77     78         if value[0] == '"' and value[-1] == '"':    79             return value[1:-1]    80         else:    81             return value    82     83     def decode_parameters(self, parameters):    84     85         """    86         Decode the given 'parameters' according to the vCalendar specification.    87         """    88     89         decoded_parameters = {}    90     91         for param_name, param_value in parameters.items():    92             if param_name in QUOTED_PARAMETERS:    93                 param_value = self.decode_quoted_value(param_value)    94                 separator = '","'    95             else:    96                 separator = ","    97             if param_name in MULTIVALUED_PARAMETERS:    98                 param_value = param_value.split(separator)    99             decoded_parameters[param_name] = param_value   100    101         return decoded_parameters   102    103 class vCalendarParser(vContent.Parser):   104    105     "A parser specifically for vCalendar/iCalendar."   106    107     def parse(self, f, parser_cls=None):   108         return vContent.Parser.parse(self, f, vCalendarStreamParser)   109    110 # Writer classes.   111    112 class vCalendarStreamWriter(vContent.StreamWriter):   113    114     "A stream writer specifically for vCard."   115    116     def write(self, name, parameters, value):   117    118         """   119         Write a content line for the given 'name', 'parameters' and 'value'   120         information.   121         """   122    123         vContent.StreamWriter.write(self, name, self.encode_parameters(parameters), value)   124    125     # Internal methods.   126    127     def encode_quoted_value(self, value):   128    129         "Encode the given 'value'."   130    131         return '"%s"' % value   132    133     def encode_parameters(self, parameters):   134    135         """   136         Encode the given 'parameters' according to the vCalendar specification.   137         """   138    139         encoded_parameters = {}   140    141         for param_name, param_value in parameters.items():   142             if param_name in QUOTED_PARAMETERS:   143                 param_value = self.encode_quoted_value(param_value)   144                 separator = '","'   145             else:   146                 separator = ","   147             if param_name in MULTIVALUED_PARAMETERS:   148                 param_value = separator.join(param_value)   149             encoded_parameters[param_name] = param_value   150    151         return encoded_parameters   152    153     def encode_content(self, value):   154    155         "Encode the given 'value', replacing separator characters."   156    157         # Replace quoted characters (see 4.3.11 in RFC 2445).   158    159         value = vContent.StreamWriter.encode_content(self, value)   160         return value.replace(";", "\\;").replace(",", "\\,")   161    162 # Public functions.   163    164 def parse(f, non_standard_newline=0):   165    166     """   167     Parse the resource data found through the use of the file object 'f', which   168     should provide Unicode data. (The codecs module can be used to open files or   169     to wrap streams in order to provide Unicode data.)   170    171     The optional 'non_standard_newline' can be set to a true value (unlike the   172     default) in order to attempt to process files with CR as the end of line   173     character.   174    175     As a result of parsing the resource, the root node of the imported resource   176     is returned.   177     """   178    179     return vContent.parse(f, non_standard_newline, vCalendarParser)   180    181 def iterparse(f, non_standard_newline=0):   182    183     """   184     Parse the resource data found through the use of the file object 'f', which   185     should provide Unicode data. (The codecs module can be used to open files or   186     to wrap streams in order to provide Unicode data.)   187    188     The optional 'non_standard_newline' can be set to a true value (unlike the   189     default) in order to attempt to process files with CR as the end of line   190     character.   191    192     An iterator is returned which provides event tuples describing parsing   193     events of the form (name, parameters, value).   194     """   195    196     return vContent.iterparse(f, non_standard_newline, vCalendarStreamParser)   197    198 def iterwrite(f, line_length=None):   199     return vContent.iterwrite(f, line_length, vCalendarStreamWriter)   200    201 # vim: tabstop=4 expandtab shiftwidth=4