diff --git a/TODO.txt b/TODO.txt index 7a6ce2c..a7860f5 100644 --- a/TODO.txt +++ b/TODO.txt @@ -1,6 +1,15 @@ Todo ==== +* unify api and make it python 3 compliant. + - deprecate usage of __str__, ical, str( + - deprecate usage of from_string, as_string, string + - use from_ical, to_ical + +- use @static instead of staticmethod + +%s/from_string/from_ical/gc + * Automatic encoding and decoding of parameter values. Most of the work is done already. Just need to get it finished. Look at line 153 in 'src/icalendar/parser.py' diff --git a/src/icalendar/cal.py b/src/icalendar/cal.py index adb4dc4..822dbd0 100644 --- a/src/icalendar/cal.py +++ b/src/icalendar/cal.py @@ -31,7 +31,7 @@ class ComponentFactory(CaselessDict): >>> factory = ComponentFactory() >>> component = factory['VEVENT'] >>> event = component(dtstart='19700101') - >>> event.as_string() + >>> event.to_ical() 'BEGIN:VEVENT\\r\\nDTSTART:19700101\\r\\nEND:VEVENT\\r\\n' >>> factory.get('VCALENDAR', Component) @@ -103,7 +103,7 @@ class Component(CaselessDict): >>> c = Component() >>> c.name = 'VCALENDAR' >>> c.add('attendee', 'Max M') - >>> c.as_string() + >>> c.to_ical() 'BEGIN:VCALENDAR\\r\\nATTENDEE:Max M\\r\\nEND:VCALENDAR\\r\\n' >>> from icalendar.prop import vDatetime @@ -113,7 +113,7 @@ class Component(CaselessDict): >>> e.name = 'VEVENT' >>> e.add('dtend', '20000102T000000', encode=0) >>> e.add('dtstart', '20000101T000000', encode=0) - >>> e.as_string() + >>> e.to_ical() 'BEGIN:VEVENT\\r\\nDTEND:20000102T000000\\r\\nDTSTART:20000101T000000\\r\\nSUMMARY:A brief history of time\\r\\nEND:VEVENT\\r\\n' >>> c.add_component(e) @@ -135,7 +135,7 @@ class Component(CaselessDict): Text fields which span multiple mulitple lines require proper indenting >>> c = Calendar() >>> c['description']=u'Paragraph one\\n\\nParagraph two' - >>> c.as_string() + >>> c.to_ical() 'BEGIN:VCALENDAR\\r\\nDESCRIPTION:Paragraph one\\r\\n \\r\\n Paragraph two\\r\\nEND:VCALENDAR\\r\\n' INLINE properties have their values on one property line. Note the double @@ -145,7 +145,7 @@ class Component(CaselessDict): >>> c VCALENDAR({'RESOURCES': 'Chair, Table, "Room: 42"'}) - >>> c.as_string() + >>> c.to_ical() 'BEGIN:VCALENDAR\\r\\nRESOURCES:Chair, Table, "Room: 42"\\r\\nEND:VCALENDAR\\r\\n' The inline values must be handled by the get_inline() and set_inline() @@ -324,7 +324,7 @@ class Component(CaselessDict): [(name, value), ...] """ vText = types_factory['text'] - properties = [('BEGIN', vText(self.name).ical())] + properties = [('BEGIN', vText(self.name).to_ical())] property_names = self.keys() property_names.sort() for name in property_names: @@ -338,17 +338,17 @@ class Component(CaselessDict): # recursion is fun! for subcomponent in self.subcomponents: properties += subcomponent.property_items() - properties.append(('END', vText(self.name).ical())) + properties.append(('END', vText(self.name).to_ical())) return properties - def from_string(st, multiple=False): + def from_ical(st, multiple=False): """ Populates the component recursively from a string """ stack = [] # a stack of components comps = [] - for line in Contentlines.from_string(st): # raw parsing + for line in Contentlines.from_ical(st): # raw parsing if not line: continue name, params, vals = line.parts() @@ -396,7 +396,7 @@ class Component(CaselessDict): raise ValueError('Found no components where ' 'exactly one is required: {st!r}'.format(**locals())) return comps[0] - from_string = staticmethod(from_string) + from_ical = staticmethod(from_ical) def __repr__(self): @@ -418,15 +418,10 @@ class Component(CaselessDict): return contentlines - def as_string(self): + def to_ical(self): return str(self.content_lines()) - def __str__(self): - "Returns rendered iCalendar" - return self.as_string() - - ####################################### # components defined in RFC 2445 @@ -536,13 +531,13 @@ class Calendar(Component): >>> event['uid'] = '42' >>> event.set('dtstart', datetime(2005,4,4,8,0,0)) >>> cal.add_component(event) - >>> cal.subcomponents[0].as_string() + >>> cal.subcomponents[0].to_ical() 'BEGIN:VEVENT\\r\\nDTSTART;VALUE=DATE-TIME:20050404T080000\\r\\nSUMMARY:Python meeting about calendaring\\r\\nUID:42\\r\\nEND:VEVENT\\r\\n' Write to disc >>> import tempfile, os >>> directory = tempfile.mkdtemp() - >>> open(os.path.join(directory, 'test.ics'), 'wb').write(cal.as_string()) + >>> open(os.path.join(directory, 'test.ics'), 'wb').write(cal.to_ical()) Parsing a complete calendar from a string will silently ignore bogus events. The bogosity in the following is the third EXDATE: it has an empty DATE. @@ -567,7 +562,7 @@ class Calendar(Component): ... 'EXDATE;VALUE=DATE:', ... 'END:VEVENT', ... 'END:VCALENDAR')) - >>> [e['DESCRIPTION'].ical() for e in Calendar.from_string(s).walk('VEVENT')] + >>> [e['DESCRIPTION'].to_ical() for e in Calendar.from_ical(s).walk('VEVENT')] ['Perfectly OK event'] """ diff --git a/src/icalendar/interfaces.py b/src/icalendar/interfaces.py index 708d05a..712fbd6 100644 --- a/src/icalendar/interfaces.py +++ b/src/icalendar/interfaces.py @@ -63,7 +63,7 @@ class IComponent(Interface): """ # static method, can be called on class directly - def from_string(st, multiple=False): + def from_ical(st, multiple=False): """Populates the component recursively from a iCalendar string. Reads the iCalendar string and constructs components and @@ -99,7 +99,7 @@ class IComponent(Interface): Returns list of python objects. """ - def as_string(): + def to_ical(): """Render the component in the RFC 2445 (iCalendar) format. Returns a string in RFC 2445 format. @@ -162,10 +162,10 @@ class IPropertyValue(Interface): This invariance should always be true: - assert x == vDataType.from_ical(vDataType(x).ical()) + assert x == vDataType.from_ical(vDataType(x).to_ical()) """ - def ical(): + def to_ical(): """Render property as string, as defined in iCalendar RFC 2445. """ @@ -173,7 +173,7 @@ class IPropertyValue(Interface): def from_ical(ical): """Parse property from iCalendar RFC 2445 text. - Inverse of ical(). + Inverse of to_ical(). """ class IBinary(IPropertyValue): diff --git a/src/icalendar/parser.py b/src/icalendar/parser.py index adaaab5..2cdf987 100644 --- a/src/icalendar/parser.py +++ b/src/icalendar/parser.py @@ -103,7 +103,7 @@ class Parameters(CaselessDict): Simple parameter:value pair >>> p = Parameters(parameter1='Value1') - >>> str(p) + >>> p.to_ical() 'PARAMETER1=Value1' @@ -121,47 +121,47 @@ class Parameters(CaselessDict): Parameter with list of values must be seperated by comma >>> p = Parameters({'parameter1':['Value1', 'Value2']}) - >>> str(p) + >>> p.to_ical() 'PARAMETER1=Value1,Value2' Multiple parameters must be seperated by a semicolon >>> p = Parameters({'RSVP':'TRUE', 'ROLE':'REQ-PARTICIPANT'}) - >>> str(p) + >>> p.to_ical() 'ROLE=REQ-PARTICIPANT;RSVP=TRUE' Parameter values containing ',;:' must be double quoted >>> p = Parameters({'ALTREP':'http://www.wiz.org'}) - >>> str(p) + >>> p.to_ical() 'ALTREP="http://www.wiz.org"' list items must be quoted seperately >>> p = Parameters({'MEMBER':['MAILTO:projectA@host.com', 'MAILTO:projectB@host.com', ]}) - >>> str(p) + >>> p.to_ical() 'MEMBER="MAILTO:projectA@host.com","MAILTO:projectB@host.com"' Now the whole sheebang >>> p = Parameters({'parameter1':'Value1', 'parameter2':['Value2', 'Value3'],\ 'ALTREP':['http://www.wiz.org', 'value4']}) - >>> str(p) + >>> p.to_ical() 'ALTREP="http://www.wiz.org",value4;PARAMETER1=Value1;PARAMETER2=Value2,Value3' We can also parse parameter strings - >>> Parameters.from_string('PARAMETER1=Value 1;param2=Value 2') + >>> Parameters.from_ical('PARAMETER1=Value 1;param2=Value 2') Parameters({'PARAMETER1': 'Value 1', 'PARAM2': 'Value 2'}) Including empty strings - >>> Parameters.from_string('param=') + >>> Parameters.from_ical('param=') Parameters({'PARAM': ''}) We can also parse parameter strings - >>> Parameters.from_string('MEMBER="MAILTO:projectA@host.com","MAILTO:projectB@host.com"') + >>> Parameters.from_ical('MEMBER="MAILTO:projectA@host.com","MAILTO:projectB@host.com"') Parameters({'MEMBER': ['MAILTO:projectA@host.com', 'MAILTO:projectB@host.com']}) We can also parse parameter strings - >>> Parameters.from_string('ALTREP="http://www.wiz.org",value4;PARAMETER1=Value1;PARAMETER2=Value2,Value3') + >>> Parameters.from_ical('ALTREP="http://www.wiz.org",value4;PARAMETER1=Value1;PARAMETER2=Value2,Value3') Parameters({'PARAMETER1': 'Value1', 'ALTREP': ['http://www.wiz.org', 'value4'], 'PARAMETER2': ['Value2', 'Value3']}) """ @@ -194,7 +194,7 @@ class Parameters(CaselessDict): return 'Parameters(' + dict.__repr__(self) + ')' - def __str__(self): + def to_ical(self): result = [] items = self.items() items.sort() # To make doctests work @@ -204,7 +204,7 @@ class Parameters(CaselessDict): return ';'.join(result) - def from_string(st, strict=False): + def from_ical(st, strict=False): "Parses the parameter format from ical text format" # parse into strings @@ -238,7 +238,7 @@ class Parameters(CaselessDict): except ValueError, e: raise ValueError, '%r is not a valid parameter string: %s' % (param, e) return result - from_string = staticmethod(from_string) + from_ical = staticmethod(from_ical) ######################################### @@ -250,36 +250,36 @@ class Contentline(str): parts. >>> c = Contentline('Si meliora dies, ut vina, poemata reddit') - >>> str(c) + >>> c.to_ical() 'Si meliora dies, ut vina, poemata reddit' A long line gets folded >>> c = Contentline(''.join(['123456789 ']*10)) - >>> str(c) + >>> c.to_ical() '123456789 123456789 123456789 123456789 123456789 123456789 123456789 1234\\r\\n 56789 123456789 123456789' A folded line gets unfolded - >>> c = Contentline.from_string(str(c)) + >>> c = Contentline.from_ical(str(c)) >>> c '123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789' Newlines in a string get need to be preserved >>> c = Contentline('1234\\n\\n1234') - >>> str(c) + >>> c.to_ical() '1234\\r\\n \\r\\n 1234' We do not fold within a UTF-8 character: >>> c = Contentline('This line has a UTF-8 character where it should be folded. Make sure it g\xc3\xabts folded before that character.') - >>> '\xc3\xab' in str(c) + >>> '\xc3\xab' in c.to_ical() True Another test of the above >>> c = Contentline('x' * 73 + '\xc3\xab' + '\\n ' + 'y' * 10) - >>> str(c).count('\xc3') + >>> c.to_ical().count('\xc3') 1 Don't fail if we fold a line that is exactly X times 74 characters long: - >>> c = str(Contentline(''.join(['x']*148))) + >>> c = Contentline(''.join(['x']*148)).to_ical() It can parse itself into parts. Which is a tuple of (name, params, vals) @@ -294,7 +294,7 @@ class Contentline(str): >>> c = Contentline('ATTENDEE;CN=Max Rasmussen;ROLE=REQ-PARTICIPANT:MAILTO:maxm@example.com') >>> c.parts() ('ATTENDEE', Parameters({'ROLE': 'REQ-PARTICIPANT', 'CN': 'Max Rasmussen'}), 'MAILTO:maxm@example.com') - >>> str(c) + >>> c.to_ical() 'ATTENDEE;CN=Max Rasmussen;ROLE=REQ-PARTICIPANT:MAILTO:maxm@example.com' and back again @@ -353,7 +353,7 @@ class Contentline(str): ('key', Parameters({'PARAM': 'pvalue'}), 'value') Should bomb on missing param: - >>> c = Contentline.from_string("k;:no param") + >>> c = Contentline.from_ical("k;:no param") >>> c.parts() #doctest: +ELLIPSIS Traceback (most recent call last): ... @@ -384,7 +384,7 @@ class Contentline(str): def from_parts(parts): "Turns a tuple of parts into a content line" - (name, params, values) = [str(p) for p in parts] + (name, params, values) = [str(p) for p in parts] # TODO: str or to_ical? try: if params: return Contentline('%s;%s:%s' % (name, params, values)) @@ -418,23 +418,23 @@ class Contentline(str): validate_token(name) if name_split+1 == value_split: raise ValueError, 'Invalid content line' - params = Parameters.from_string(self[name_split+1:value_split], + params = Parameters.from_ical(self[name_split+1:value_split], strict=self.strict) values = self[value_split+1:] return (name, params, values) except ValueError, e: raise ValueError, "Content line could not be parsed into parts: %r: %s" % (self, e) - def from_string(st, strict=False): + def from_ical(st, strict=False): "Unfolds the content lines in an iCalendar into long content lines" try: # a fold is carriage return followed by either a space or a tab return Contentline(FOLD.sub('', st), strict=strict) except: raise ValueError, 'Expected StringType with content line' - from_string = staticmethod(from_string) + from_ical = staticmethod(from_ical) - def __str__(self): + def to_ical(self): "Long content lines are folded so they are less than 75 characters wide" l_line = len(self) new_lines = [] @@ -480,29 +480,29 @@ class Contentlines(list): used instead. >>> c = Contentlines([Contentline('BEGIN:VEVENT\\r\\n')]) - >>> str(c) + >>> c.to_ical() 'BEGIN:VEVENT\\r\\n' Lets try appending it with a 100 charater wide string >>> c.append(Contentline(''.join(['123456789 ']*10)+'\\r\\n')) - >>> str(c) + >>> c.to_ical() 'BEGIN:VEVENT\\r\\n\\r\\n123456789 123456789 123456789 123456789 123456789 123456789 123456789 1234\\r\\n 56789 123456789 123456789 \\r\\n' Notice that there is an extra empty string in the end of the content lines. That is so they can be easily joined with: '\r\n'.join(contentlines)). - >>> Contentlines.from_string('A short line\\r\\n') + >>> Contentlines.from_ical('A short line\\r\\n') ['A short line', ''] - >>> Contentlines.from_string('A faked\\r\\n long line\\r\\n') + >>> Contentlines.from_ical('A faked\\r\\n long line\\r\\n') ['A faked long line', ''] - >>> Contentlines.from_string('A faked\\r\\n long line\\r\\nAnd another lin\\r\\n\\te that is folded\\r\\n') + >>> Contentlines.from_ical('A faked\\r\\n long line\\r\\nAnd another lin\\r\\n\\te that is folded\\r\\n') ['A faked long line', 'And another line that is folded', ''] """ - def __str__(self): + def to_ical(self): "Simply join self." return '\r\n'.join(map(str, self)) - def from_string(st): + def from_ical(st): "Parses a string into content lines" try: # a fold is carriage return followed by either a space or a tab @@ -512,12 +512,12 @@ class Contentlines(list): return Contentlines(lines) except: raise ValueError, 'Expected StringType with content lines' - from_string = staticmethod(from_string) + from_ical = staticmethod(from_ical) # ran this: # sample = open('./samples/test.ics', 'rb').read() # binary file in windows! -# lines = Contentlines.from_string(sample) +# lines = Contentlines.from_ical(sample) # for line in lines[:-1]: # print line.parts() diff --git a/src/icalendar/prop.py b/src/icalendar/prop.py index 587978a..f00e184 100644 --- a/src/icalendar/prop.py +++ b/src/icalendar/prop.py @@ -23,18 +23,18 @@ for the classes/datatypes that are used in Icalendar: iCalendar properties has values. The values are strongly typed. This module -defines these types, calling val.ical() on them, Will render them as defined in +defines these types, calling val.to_ical() on them, Will render them as defined in rfc2445. If you pass any of these classes a Python primitive, you will have an object that can render itself as iCalendar formatted date. -Property Value Data Types starts with a 'v'. they all have an ical() and -from_ical() method. The ical() method generates a text string in the iCalendar +Property Value Data Types starts with a 'v'. they all have an to_ical() and +from_ical() method. The to_ical() method generates a text string in the iCalendar format. The from_ical() method can parse this format and return a primitive Python datatype. So it should allways be true that: - x == vDataType.from_ical(VDataType(x).ical()) + x == vDataType.from_ical(VDataType(x).to_ical()) These types are mainly used for parsing and file generation. But you can set them directly. @@ -66,7 +66,7 @@ class vBinary: """ Binary property values are base 64 encoded >>> b = vBinary('This is gibberish') - >>> b.ical() + >>> b.to_ical() 'VGhpcyBpcyBnaWJiZXJpc2g=' >>> b = vBinary.from_ical('VGhpcyBpcyBnaWJiZXJpc2g=') >>> b @@ -74,7 +74,7 @@ class vBinary: The roundtrip test >>> x = 'Binary data æ ø å \x13 \x56' - >>> vBinary(x).ical() + >>> vBinary(x).to_ical() 'QmluYXJ5IGRhdGEg5iD4IOUgEyBW' >>> vBinary.from_ical('QmluYXJ5IGRhdGEg5iD4IOUgEyBW') 'Binary data \\xe6 \\xf8 \\xe5 \\x13 V' @@ -85,7 +85,7 @@ class vBinary: Long data should not have line breaks, as that would interfere >>> x = 'a'*99 - >>> vBinary(x).ical() == 'YWFh' * 33 + >>> vBinary(x).to_ical() == 'YWFh' * 33 True >>> vBinary.from_ical('YWFh' * 33) == 'a' * 99 True @@ -99,7 +99,7 @@ class vBinary: def __repr__(self): return "vBinary(%s)" % str.__repr__(self.obj) - def ical(self): + def to_ical(self): return binascii.b2a_base64(self.obj)[:-1] def from_ical(ical): @@ -110,24 +110,21 @@ class vBinary: raise ValueError, 'Not valid base 64 encoding.' from_ical = staticmethod(from_ical) - def __str__(self): - return self.ical() - class vBoolean(int): """ Returns specific string according to state >>> bin = vBoolean(True) - >>> bin.ical() + >>> bin.to_ical() 'TRUE' >>> bin = vBoolean(0) - >>> bin.ical() + >>> bin.to_ical() 'FALSE' The roundtrip test >>> x = True - >>> x == vBoolean.from_ical(vBoolean(x).ical()) + >>> x == vBoolean.from_ical(vBoolean(x).to_ical()) True >>> vBoolean.from_ical('true') True @@ -138,7 +135,7 @@ class vBoolean(int): self.params = Parameters() return self - def ical(self): + def to_ical(self): if self: return 'TRUE' return 'FALSE' @@ -153,8 +150,6 @@ class vBoolean(int): raise ValueError, "Expected 'TRUE' or 'FALSE'. Got %s" % ical from_ical = staticmethod(from_ical) - def __str__(self): - return self.ical() @@ -163,9 +158,7 @@ class vCalAddress(str): This just returns an unquoted string >>> a = vCalAddress('MAILTO:maxm@mxm.dk') >>> a.params['cn'] = 'Max M' - >>> a.ical() - 'MAILTO:maxm@mxm.dk' - >>> str(a) + >>> a.to_ical() 'MAILTO:maxm@mxm.dk' >>> a.params Parameters({'CN': 'Max M'}) @@ -181,7 +174,7 @@ class vCalAddress(str): def __repr__(self): return u"vCalAddress(%s)" % str.__repr__(self) - def ical(self): + def to_ical(self): return str(self) def from_ical(ical): @@ -192,8 +185,6 @@ class vCalAddress(str): raise ValueError, 'Expected vCalAddress, got: %s' % ical from_ical = staticmethod(from_ical) - def __str__(self): - return str.__str__(self) #################################################### # handy tzinfo classes you can use. @@ -280,14 +271,14 @@ class vDatetime: >>> d = datetime(2001, 1,1, 12, 30, 0) >>> dt = vDatetime(d) - >>> dt.ical() + >>> dt.to_ical() '20010101T123000' >>> vDatetime.from_ical('20000101T120000') datetime.datetime(2000, 1, 1, 12, 0) >>> dutc = datetime(2001, 1,1, 12, 30, 0, tzinfo=UTC) - >>> vDatetime(dutc).ical() + >>> vDatetime(dutc).to_ical() '20010101T123000Z' >>> vDatetime.from_ical('20010101T000000') @@ -299,7 +290,7 @@ class vDatetime: ValueError: Wrong datetime format: 20010101T000000A >>> utc = vDatetime.from_ical('20010101T000000Z') - >>> vDatetime(utc).ical() + >>> vDatetime(utc).to_ical() '20010101T000000Z' """ @@ -307,7 +298,7 @@ class vDatetime: self.dt = dt self.params = Parameters() - def ical(self): + def to_ical(self): if self.dt.tzinfo: utc_time = self.dt - self.dt.tzinfo.utcoffset(self.dt) return utc_time.strftime("%Y%m%dT%H%M%SZ") @@ -335,8 +326,6 @@ class vDatetime: raise ValueError, 'Wrong datetime format: %s' % ical from_ical = staticmethod(from_ical) - def __str__(self): - return self.ical() @@ -344,13 +333,13 @@ class vDate: """ Render and generates iCalendar date format. >>> d = date(2001, 1,1) - >>> vDate(d).ical() + >>> vDate(d).to_ical() '20010101' >>> vDate.from_ical('20010102') datetime.date(2001, 1, 2) - >>> vDate('d').ical() + >>> vDate('d').to_ical() Traceback (most recent call last): ... ValueError: Value MUST be a date instance @@ -362,7 +351,7 @@ class vDate: self.dt = dt self.params = Parameters(dict(value='DATE')) - def ical(self): + def to_ical(self): return self.dt.strftime("%Y%m%d") def from_ical(ical): @@ -378,8 +367,6 @@ class vDate: raise ValueError, 'Wrong date format %s' % ical from_ical = staticmethod(from_ical) - def __str__(self): - return self.ical() @@ -387,32 +374,32 @@ class vDuration: """ Subclass of timedelta that renders itself in the iCalendar DURATION format. - >>> vDuration(timedelta(11)).ical() + >>> vDuration(timedelta(11)).to_ical() 'P11D' - >>> vDuration(timedelta(-14)).ical() + >>> vDuration(timedelta(-14)).to_ical() '-P14D' - >>> vDuration(timedelta(1, 7384)).ical() + >>> vDuration(timedelta(1, 7384)).to_ical() 'P1DT2H3M4S' - >>> vDuration(timedelta(1, 7380)).ical() + >>> vDuration(timedelta(1, 7380)).to_ical() 'P1DT2H3M' - >>> vDuration(timedelta(1, 7200)).ical() + >>> vDuration(timedelta(1, 7200)).to_ical() 'P1DT2H' - >>> vDuration(timedelta(0, 7200)).ical() + >>> vDuration(timedelta(0, 7200)).to_ical() 'PT2H' - >>> vDuration(timedelta(0, 7384)).ical() + >>> vDuration(timedelta(0, 7384)).to_ical() 'PT2H3M4S' - >>> vDuration(timedelta(0, 184)).ical() + >>> vDuration(timedelta(0, 184)).to_ical() 'PT3M4S' - >>> vDuration(timedelta(0, 22)).ical() + >>> vDuration(timedelta(0, 22)).to_ical() 'PT22S' - >>> vDuration(timedelta(0, 3622)).ical() + >>> vDuration(timedelta(0, 3622)).to_ical() 'PT1H0M22S' - >>> vDuration(timedelta(days=1, hours=5)).ical() + >>> vDuration(timedelta(days=1, hours=5)).to_ical() 'P1DT5H' - >>> vDuration(timedelta(hours=-5)).ical() + >>> vDuration(timedelta(hours=-5)).to_ical() '-PT5H' - >>> vDuration(timedelta(days=-1, hours=-5)).ical() + >>> vDuration(timedelta(days=-1, hours=-5)).to_ical() '-P1DT5H' How does the parsing work? @@ -439,7 +426,7 @@ class vDuration: self.td = td self.params = Parameters() - def ical(self): + def to_ical(self): sign = "" if self.td.days < 0: sign = "-" @@ -482,8 +469,6 @@ class vDuration: raise ValueError('Invalid iCalendar duration: %s' % ical) from_ical = staticmethod(from_ical) - def __str__(self): - return self.ical() @@ -491,11 +476,11 @@ class vFloat(float): """ Just a float. >>> f = vFloat(1.0) - >>> f.ical() + >>> f.to_ical() '1.0' >>> vFloat.from_ical('42') 42.0 - >>> vFloat(42).ical() + >>> vFloat(42).to_ical() '42.0' """ @@ -504,7 +489,7 @@ class vFloat(float): self.params = Parameters() return self - def ical(self): + def to_ical(self): return str(self) def from_ical(ical): @@ -521,7 +506,7 @@ class vInt(int): """ Just an int. >>> f = vInt(42) - >>> f.ical() + >>> f.to_ical() '42' >>> vInt.from_ical('13') 13 @@ -536,7 +521,7 @@ class vInt(int): self.params = Parameters() return self - def ical(self): + def to_ical(self): return str(self) def from_ical(ical): @@ -593,14 +578,14 @@ class vDDDTypes: self.dt = dt - def ical(self): + def to_ical(self): dt = self.dt if isinstance(dt, datetime): - return vDatetime(dt).ical() + return vDatetime(dt).to_ical() elif isinstance(dt, date): - return vDate(dt).ical() + return vDate(dt).to_ical() elif isinstance(dt, timedelta): - return vDuration(dt).ical() + return vDuration(dt).to_ical() else: raise ValueError('Unknown date type') @@ -615,8 +600,6 @@ class vDDDTypes: return vDate.from_ical(ical) from_ical = staticmethod(from_ical) - def __str__(self): - return self.ical() class vDDDLists: @@ -671,11 +654,11 @@ class vDDDLists: vDDD.append(vDDDTypes(dt)) self.dts = vDDD - def ical(self): + def to_ical(self): ''' Generates the text string in the iCalendar format. ''' - dts_ical = [dt.ical() for dt in self.dts] + dts_ical = [dt.to_ical() for dt in self.dts] return ",".join(dts_ical) def from_ical(ical): @@ -690,8 +673,6 @@ class vDDDLists: return out from_ical = staticmethod(from_ical) - def __str__(self): - return self.ical() class vPeriod: @@ -777,7 +758,7 @@ class vPeriod: return True return False - def ical(self): + def to_ical(self): if self.by_duration: return '%s/%s' % (vDatetime(self.start).ical(), vDuration(self.duration).ical()) return '%s/%s' % (vDatetime(self.start).ical(), vDatetime(self.end).ical()) @@ -793,8 +774,6 @@ class vPeriod: raise ValueError, 'Expected period format, got: %s' % ical from_ical = staticmethod(from_ical) - def __str__(self): - return self.ical() def __repr__(self): if self.by_duration: @@ -807,7 +786,7 @@ class vWeekday(str): """ This returns an unquoted weekday abbrevation >>> a = vWeekday('mo') - >>> a.ical() + >>> a.to_ical() 'MO' >>> a = vWeekday('erwer') @@ -827,15 +806,15 @@ class vWeekday(str): ValueError: Expected weekday abbrevation, got: Saturday >>> a = vWeekday('+mo') - >>> a.ical() + >>> a.to_ical() '+MO' >>> a = vWeekday('+3mo') - >>> a.ical() + >>> a.to_ical() '+3MO' >>> a = vWeekday('-tu') - >>> a.ical() + >>> a.to_ical() '-TU' """ @@ -857,7 +836,7 @@ class vWeekday(str): self.params = Parameters() return self - def ical(self): + def to_ical(self): return self.upper() def from_ical(ical): @@ -868,8 +847,6 @@ class vWeekday(str): raise ValueError, 'Expected weekday abbrevation, got: %s' % ical from_ical = staticmethod(from_ical) - def __str__(self): - return self.ical() @@ -880,7 +857,7 @@ class vFrequency(str): Traceback (most recent call last): ... ValueError: Expected frequency, got: BAD TEST - >>> vFrequency('daily').ical() + >>> vFrequency('daily').to_ical() 'DAILY' >>> vFrequency('daily').from_ical('MONTHLY') 'MONTHLY' @@ -903,7 +880,7 @@ class vFrequency(str): self.params = Parameters() return self - def ical(self): + def to_ical(self): return self.upper() def from_ical(ical): @@ -914,8 +891,6 @@ class vFrequency(str): raise ValueError, 'Expected weekday abbrevation, got: %s' % ical from_ical = staticmethod(from_ical) - def __str__(self): - return self.ical() @@ -930,7 +905,7 @@ class vRecur(CaselessDict): >>> r['byhour'] = [8,9] >>> r['byminute'] = 30 >>> r = vRecur(r) - >>> r.ical() + >>> r.to_ical() 'FREQ=YEARLY;INTERVAL=2;BYMINUTE=30;BYHOUR=8,9;BYDAY=SU;BYMONTH=1' >>> r = vRecur(FREQ='yearly', INTERVAL=2) @@ -938,39 +913,39 @@ class vRecur(CaselessDict): >>> r['BYDAY'] = 'su' >>> r['BYHOUR'] = [8,9] >>> r['BYMINUTE'] = 30 - >>> r.ical() + >>> r.to_ical() 'FREQ=YEARLY;INTERVAL=2;BYMINUTE=30;BYHOUR=8,9;BYDAY=SU;BYMONTH=1' >>> r = vRecur(freq='DAILY', count=10) >>> r['bysecond'] = [0, 15, 30, 45] - >>> r.ical() + >>> r.to_ical() 'FREQ=DAILY;COUNT=10;BYSECOND=0,15,30,45' >>> r = vRecur(freq='DAILY', until=datetime(2005,1,1,12,0,0)) - >>> r.ical() + >>> r.to_ical() 'FREQ=DAILY;UNTIL=20050101T120000' How do we fare with regards to parsing? >>> r = vRecur.from_ical('FREQ=DAILY;INTERVAL=2;COUNT=10') >>> r {'COUNT': [10], 'FREQ': ['DAILY'], 'INTERVAL': [2]} - >>> vRecur(r).ical() + >>> vRecur(r).to_ical() 'FREQ=DAILY;COUNT=10;INTERVAL=2' >>> r = vRecur.from_ical('FREQ=YEARLY;INTERVAL=2;BYMONTH=1;BYDAY=-SU;BYHOUR=8,9;BYMINUTE=30') >>> r {'BYHOUR': [8, 9], 'BYDAY': ['-SU'], 'BYMINUTE': [30], 'BYMONTH': [1], 'FREQ': ['YEARLY'], 'INTERVAL': [2]} - >>> vRecur(r).ical() + >>> vRecur(r).to_ical() 'FREQ=YEARLY;INTERVAL=2;BYMINUTE=30;BYHOUR=8,9;BYDAY=-SU;BYMONTH=1' Some examples from the spec >>> r = vRecur.from_ical('FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-1') - >>> vRecur(r).ical() + >>> vRecur(r).to_ical() 'FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-1' >>> r = vRecur.from_ical('FREQ=YEARLY;INTERVAL=2;BYMONTH=1;BYDAY=SU;BYHOUR=8,9;BYMINUTE=30') - >>> vRecur(r).ical() + >>> vRecur(r).to_ical() 'FREQ=YEARLY;INTERVAL=2;BYMINUTE=30;BYHOUR=8,9;BYDAY=SU;BYMONTH=1' and some errors @@ -1008,14 +983,14 @@ class vRecur(CaselessDict): CaselessDict.__init__(self, *args, **kwargs) self.params = Parameters() - def ical(self): + def to_ical(self): # SequenceTypes result = [] for key, vals in self.sorted_items(): typ = self.types[key] if not type(vals) in SequenceTypes: vals = [vals] - vals = ','.join([typ(val).ical() for val in vals]) + vals = ','.join([typ(val).to_ical() for val in vals]) result.append('%s=%s' % (key, vals)) return ';'.join(result) @@ -1042,37 +1017,34 @@ class vRecur(CaselessDict): raise ValueError, 'Error in recurrence rule: %s' % ical from_ical = staticmethod(from_ical) - def __str__(self): - return self.ical() - class vText(unicode): """ Simple text >>> t = vText(u'Simple text') - >>> t.ical() + >>> t.to_ical() 'Simple text' Escaped text >>> t = vText('Text ; with escaped, chars') - >>> t.ical() + >>> t.to_ical() 'Text \\\\; with escaped\\\\, chars' Escaped newlines - >>> vText('Text with escaped\N chars').ical() + >>> vText('Text with escaped\N chars').to_ical() 'Text with escaped\\\\n chars' If you pass a unicode object, it will be utf-8 encoded. As this is the (only) standard that RFC 2445 support. >>> t = vText(u'international chars æøå ÆØÅ ü') - >>> t.ical() + >>> t.to_ical() 'international chars \\xc3\\xa6\\xc3\\xb8\\xc3\\xa5 \\xc3\\x86\\xc3\\x98\\xc3\\x85 \\xc3\\xbc' Unicode is converted to utf-8 >>> t = vText(u'international æ ø å') - >>> str(t) + >>> t.to_ical() 'international \\xc3\\xa6 \\xc3\\xb8 \\xc3\\xa5' and parsing? @@ -1115,7 +1087,7 @@ class vText(unicode): def __repr__(self): return u"vText(%s)" % unicode.__repr__(self) - def ical(self): + def to_ical(self): return self.escape().encode(self.encoding) def from_ical(ical): @@ -1132,8 +1104,6 @@ class vText(unicode): raise ValueError, 'Expected ical text, got: %s' % ical from_ical = staticmethod(from_ical) - def __str__(self): - return self.ical() @@ -1142,7 +1112,7 @@ class vTime(time): A subclass of datetime, that renders itself in the iCalendar time format. >>> dt = vTime(12, 30, 0) - >>> dt.ical() + >>> dt.to_ical() '123000' >>> vTime.from_ical('123000') @@ -1160,7 +1130,7 @@ class vTime(time): self.params = Parameters() return self - def ical(self): + def to_ical(self): return self.strftime("%H%M%S") def from_ical(ical): @@ -1172,8 +1142,6 @@ class vTime(time): raise ValueError, 'Expected time, got: %s' % ical from_ical = staticmethod(from_ical) - def __str__(self): - return self.ical() @@ -1181,7 +1149,7 @@ class vUri(str): """ Uniform resource identifier is basically just an unquoted string. >>> u = vUri('http://www.example.com/') - >>> u.ical() + >>> u.to_ical() 'http://www.example.com/' >>> vUri.from_ical('http://www.example.com/') # doh! 'http://www.example.com/' @@ -1192,7 +1160,7 @@ class vUri(str): self.params = Parameters() return self - def ical(self): + def to_ical(self): return str(self) def from_ical(ical): @@ -1203,8 +1171,6 @@ class vUri(str): raise ValueError, 'Expected , got: %s' % ical from_ical = staticmethod(from_ical) - def __str__(self): - return str.__str__(self) @@ -1213,17 +1179,17 @@ class vGeo: A special type that is only indirectly defined in the rfc. >>> g = vGeo((1.2, 3.0)) - >>> g.ical() + >>> g.to_ical() '1.2;3.0' >>> g = vGeo.from_ical('37.386013;-122.082932') >>> g == (float('37.386013'), float('-122.082932')) True - >>> vGeo(g).ical() + >>> vGeo(g).to_ical() '37.386013;-122.082932' - >>> vGeo('g').ical() + >>> vGeo('g').to_ical() Traceback (most recent call last): ... ValueError: Input must be (float, float) for latitude and longitude @@ -1240,7 +1206,7 @@ class vGeo: self.longitude = longitude self.params = Parameters() - def ical(self): + def to_ical(self): return '%s;%s' % (self.latitude, self.longitude) def from_ical(ical): @@ -1252,8 +1218,6 @@ class vGeo: raise ValueError, "Expected 'float;float' , got: %s" % ical from_ical = staticmethod(from_ical) - def __str__(self): - return self.ical() @@ -1262,27 +1226,27 @@ class vUTCOffset: Renders itself as a utc offset >>> u = vUTCOffset(timedelta(hours=2)) - >>> u.ical() + >>> u.to_ical() '+0200' >>> u = vUTCOffset(timedelta(hours=-5)) - >>> u.ical() + >>> u.to_ical() '-0500' >>> u = vUTCOffset(timedelta()) - >>> u.ical() + >>> u.to_ical() '+0000' >>> u = vUTCOffset(timedelta(minutes=-30)) - >>> u.ical() + >>> u.to_ical() '-0030' - >>> u = vUTCOffset(timedelta(hours=2, minutes=-30)) - >>> u.ical() + ->>> u = vUTCOffset(timedelta(hours=2, minutes=-30)) + >>> u.to_ical() '+0130' >>> u = vUTCOffset(timedelta(hours=1, minutes=30)) - >>> u.ical() + >>> u.to_ical() '+0130' Parsing @@ -1297,7 +1261,7 @@ class vUTCOffset: datetime.timedelta(0, 7200) >>> o = vUTCOffset.from_ical('+0230') - >>> vUTCOffset(o).ical() + >>> vUTCOffset(o).to_ical() '+0230' And a few failures @@ -1318,7 +1282,7 @@ class vUTCOffset: self.td = td self.params = Parameters() - def ical(self): + def to_ical(self): td = self.td day_in_minutes = (td.days * 24 * 60) seconds_in_minutes = td.seconds // 60 @@ -1348,8 +1312,6 @@ class vUTCOffset: return offset from_ical = staticmethod(from_ical) - def __str__(self): - return self.ical() @@ -1376,15 +1338,13 @@ class vInline(str): self.obj = obj self.params = Parameters() - def ical(self): + def to_ical(self): return str(self) def from_ical(ical): return str(ical) from_ical = staticmethod(from_ical) - def __str__(self): - return str(self.obj) class TypesFactory(CaselessDict): @@ -1394,7 +1354,7 @@ class TypesFactory(CaselessDict): >>> factory = TypesFactory() >>> datetime_parser = factory['date-time'] >>> dt = datetime_parser(datetime(2001, 1, 1)) - >>> dt.ical() + >>> dt.to_ical() '20010101T000000' A typical use is when the parser tries to find a content type and use text @@ -1406,12 +1366,12 @@ class TypesFactory(CaselessDict): datetime.datetime(2005, 1, 1, 12, 30) It can also be used to directly encode property and parameter values - >>> comment = factory.ical('comment', u'by Rasmussen, Max Møller') + >>> comment = factory.to_ical('comment', u'by Rasmussen, Max Møller') >>> str(comment) 'by Rasmussen\\\\, Max M\\xc3\\xb8ller' - >>> factory.ical('priority', 1) + >>> factory.to_ical('priority', 1) '1' - >>> factory.ical('cn', u'Rasmussen, Max Møller') + >>> factory.to_ical('cn', u'Rasmussen, Max Møller') 'Rasmussen\\\\, Max M\\xc3\\xb8ller' >>> factory.from_ical('cn', 'Rasmussen\\\\, Max M\\xc3\\xb8ller') @@ -1535,13 +1495,13 @@ class TypesFactory(CaselessDict): "Returns a the default type for a property or parameter" return self[self.types_map.get(name, 'text')] - def ical(self, name, value): + def to_ical(self, name, value): """ Encodes a named value from a primitive python type to an icalendar encoded string. """ type_class = self.for_property(name) - return type_class(value).ical() + return type_class(value).to_ical() def from_ical(self, name, value): """ diff --git a/src/icalendar/tests/example.txt b/src/icalendar/tests/example.txt index 62e51db..6d84ddf 100644 --- a/src/icalendar/tests/example.txt +++ b/src/icalendar/tests/example.txt @@ -79,13 +79,9 @@ you do it like this. The calendar is a component:: ('DTSTART', '20050404T080000') ('SUMMARY', 'Python meeting about calendaring') -You can generate a string for a file with the as_string() method. (Calling -str(cal) does the same):: +You can generate a string for a file with the to_ical() method:: - >>> cal.as_string() - 'BEGIN:VCALENDAR\r\nDTSTART:20050404T080000\r\nSUMMARY:Python meeting about calendaring\r\nEND:VCALENDAR\r\n' - - >>> str(cal) + >>> cal.to_ical() 'BEGIN:VCALENDAR\r\nDTSTART:20050404T080000\r\nSUMMARY:Python meeting about calendaring\r\nEND:VCALENDAR\r\n' The rendered view is easier to read:: @@ -95,10 +91,10 @@ The rendered view is easier to read:: SUMMARY:Python meeting about calendaring END:VCALENDAR -So, let's define a function so we can easily display to_string() output:: +So, let's define a function so we can easily display to_ical() output:: >>> def display(cal): - ... return cal.as_string().replace('\r\n', '\n').strip() + ... return cal.to_ical().replace('\r\n', '\n').strip() You can set multiple properties like this:: @@ -177,7 +173,7 @@ type defined in the spec:: >>> from datetime import datetime >>> cal.add('dtstart', datetime(2005,4,4,8,0,0)) - >>> str(cal['dtstart']) + >>> cal['dtstart'].to_ical() '20050404T080000' If that doesn't work satisfactorily for some reason, you can also do it @@ -190,11 +186,11 @@ So if you want to do it manually:: >>> from icalendar import vDatetime >>> now = datetime(2005,4,4,8,0,0) - >>> vDatetime(now).ical() + >>> vDatetime(now).to_ical() '20050404T080000' So the drill is to initialise the object with a python built in type, -and then call the "ical()" method on the object. That will return an +and then call the "to_ical()" method on the object. That will return an ical encoded string. You can do it the other way around too. To parse an encoded string, just call @@ -213,7 +209,7 @@ value directly:: >>> cal = Calendar() >>> cal.add('dtstart', datetime(2005,4,4,8,0,0)) - >>> str(cal['dtstart']) + >>> cal['dtstart'].to_ical() '20050404T080000' >>> cal.decoded('dtstart') datetime.datetime(2005, 4, 4, 8, 0) @@ -279,7 +275,7 @@ Write to disk:: >>> import tempfile, os >>> directory = tempfile.mkdtemp() >>> f = open(os.path.join(directory, 'example.ics'), 'wb') - >>> f.write(cal.as_string()) + >>> f.write(cal.to_ical()) >>> f.close() XXX We should check whether the write succeeded here.. diff --git a/src/icalendar/tests/groupscheduled.txt b/src/icalendar/tests/groupscheduled.txt index 586ffa9..8894cac 100644 --- a/src/icalendar/tests/groupscheduled.txt +++ b/src/icalendar/tests/groupscheduled.txt @@ -3,7 +3,7 @@ An example from the RFC 2445 spec:: >>> from icalendar import Calendar >>> import os >>> directory = os.path.dirname(__file__) - >>> cal = Calendar.from_string( + >>> cal = Calendar.from_ical( ... open(os.path.join(directory, 'groupscheduled.ics'),'rb').read()) >>> cal VCALENDAR({'VERSION': vText(u'2.0'), 'PRODID': vText(u'-//RDU Software//NONSGML HandCal//EN')}) diff --git a/src/icalendar/tests/multiple.txt b/src/icalendar/tests/multiple.txt index eccbd19..9c6168e 100644 --- a/src/icalendar/tests/multiple.txt +++ b/src/icalendar/tests/multiple.txt @@ -3,7 +3,7 @@ A exmaple with multiple VCALENDAR components:: >>> from icalendar import Calendar >>> import os >>> directory = os.path.dirname(__file__) - >>> cals = Calendar.from_string( + >>> cals = Calendar.from_ical( ... open(os.path.join(directory, 'multiple.ics'),'rb').read(), multiple=True) >>> for cal in cals: diff --git a/src/icalendar/tests/recurrence.txt b/src/icalendar/tests/recurrence.txt index cdd4b77..5a5f348 100644 --- a/src/icalendar/tests/recurrence.txt +++ b/src/icalendar/tests/recurrence.txt @@ -3,7 +3,7 @@ Testing recurrence. >>> from icalendar import Calendar >>> import os >>> directory = os.path.dirname(__file__) - >>> cal = Calendar.from_string( + >>> cal = Calendar.from_ical( ... open(os.path.join(directory, 'recurrence.ics'),'rb').read()) >>> first_event = cal.walk('vevent')[0] diff --git a/src/icalendar/tests/small.txt b/src/icalendar/tests/small.txt index 5afa333..342b7b6 100644 --- a/src/icalendar/tests/small.txt +++ b/src/icalendar/tests/small.txt @@ -3,7 +3,7 @@ A small example:: >>> from icalendar import Calendar >>> import os >>> directory = os.path.dirname(__file__) - >>> cal = Calendar.from_string( + >>> cal = Calendar.from_ical( ... open(os.path.join(directory, 'small.ics'),'rb').read()) >>> cal VCALENDAR({'VERSION': vText(u'2.0'), 'METHOD': vText(u'Request'), 'PRODID': vText(u'-//My product//mxm.dk/')}) diff --git a/src/icalendar/tests/test_icalendar.py b/src/icalendar/tests/test_icalendar.py index 128834e..bc277be 100644 --- a/src/icalendar/tests/test_icalendar.py +++ b/src/icalendar/tests/test_icalendar.py @@ -6,14 +6,14 @@ class FuckYouTests(unittest.TestCase): from icalendar import Calendar c = Calendar() c['description']=u'Paragraph one\n\nParagraph two' - output = c.as_string() + output = c.to_ical() self.assertEqual(output, "BEGIN:VCALENDAR\r\nDESCRIPTION:Paragraph one\r\n \r\n Paragraph two\r\nEND:VCALENDAR\r\n") def XtestTrailingNewline(self): from icalendar.parser import Contentlines, Contentline c = Contentlines([Contentline('BEGIN:VEVENT\\r\\n')]) - output = str(c) + output = c.to_ical() self.assertEqual(output, 'BEGIN:VEVENT\\r\\n') def XtestLongLine(self): @@ -21,7 +21,7 @@ class FuckYouTests(unittest.TestCase): c = Contentlines([Contentline('BEGIN:VEVENT\\r\\n')]) c.append(Contentline(''.join(['123456789 ']*10)+'\\r\\n')) import pdb ; pdb.set_trace() - output = str(c) + output = c.to_ical() self.assertEqual(output, "BEGIN:VEVENT\\r\\n\\r\\n123456789 123456789 123456789 123456789 123456789 123456789 123456789 1234\\r\\n 56789 123456789 123456789 \\r\\n") @@ -29,7 +29,7 @@ class FuckYouTests(unittest.TestCase): from icalendar.parser import Contentlines, Contentline c = Contentlines([Contentline('BEGIN:VEVENT\r\n')]) c.append(Contentline(''.join(['123456789 ']*10)+'\r\n')) - output = str(c) + output = c.to_ical() self.assertEqual(output, 'BEGIN:VEVENT\r\n\r\n123456789 123456789 123456789 123456789 123456789 123456789 123456789 1234\r\n 56789 123456789 123456789 \r\n')