4 r"""An iCalendar feed (:RFC:`5545`)
6 >>> f = Feed(url='http://example.com/calendar.ics')
8 <Feed url:http://example.com/calendar.ics>
12 We can't fetch this dummy url, so load the content by hand.
14 >>> f.content = '\r\n'.join([
15 ... 'BEGIN:VCALENDAR',
17 ... 'PRODID:-//Example Calendar//NONSGML v1.0//EN',
19 ... 'UID:2013-06-30@geohash.invalid',
20 ... 'DTSTAMP:2013-06-30T00:00:00Z',
21 ... 'DTSTART;VALUE=DATE:20130630',
22 ... 'DTEND;VALUE=DATE:20130701',
23 ... 'SUMMARY:XKCD geohashing, Boston graticule',
24 ... 'URL:http://xkcd.com/426/',
25 ... 'LOCATION:Snow Hill, Dover, Massachusetts',
26 ... 'GEO:42.226663,-71.28676',
34 PRODID:-//Example Calendar//NONSGML v1.0//EN
36 UID:2013-06-30@geohash.invalid
37 DTSTAMP:2013-06-30T00:00:00Z
38 DTSTART;VALUE=DATE:20130630
39 DTEND;VALUE=DATE:20130701
40 SUMMARY:XKCD geohashing, Boston graticule
41 URL:http://xkcd.com/426/
42 LOCATION:Snow Hill, Dover, Massachusetts
43 GEO:42.226663,-71.28676
47 To get the CRLF line endings specified in :RFC:`5545`, use the
51 >>> stream = io.StringIO()
52 >>> f.write(stream=stream)
53 >>> stream.getvalue() # doctest: +ELLIPSIS
54 'BEGIN:VCALENDAR\r\nVERSION:2.0\r\n...END:VCALENDAR\r\n'
56 def __init__(self, url, content=None):
58 self.content = content
62 return self.content.replace('\r\n', '\n').strip()
66 return '<{} url:{}>'.format(type(self).__name__, self.url)
69 raise NotImplementedError()
71 def write(self, stream):
72 stream.write(self.content)