From 43d36676eb8312d12aa50f124fd5f49902db10dd Mon Sep 17 00:00:00 2001 From: Guido Guenther Date: Fri, 8 Aug 2008 12:12:54 +0200 Subject: Imported Upstream version 0.7.1 --- PKG-INFO | 28 ++++++++------ setup.py | 33 ++++++++++------- test_files/more_tests.txt | 28 ++++++++++++-- test_files/ms_tzid.ics | 39 ++++++++++++++++++++ test_files/ruby_rrule.ics | 16 ++++++++ test_vobject.py | 12 +++--- vobject.egg-info/PKG-INFO | 28 ++++++++------ vobject.egg-info/SOURCES.txt | 3 ++ vobject.egg-info/entry_points.txt | 1 + vobject/base.py | 29 +++++++++++++++ vobject/change_tz.py | 77 +++++++++++++++++++++++++++++++++++++++ vobject/icalendar.py | 24 +++++++++--- 12 files changed, 270 insertions(+), 48 deletions(-) create mode 100644 test_files/ms_tzid.ics create mode 100644 test_files/ruby_rrule.ics create mode 100644 vobject/change_tz.py diff --git a/PKG-INFO b/PKG-INFO index 3353477..2d7c261 100644 --- a/PKG-INFO +++ b/PKG-INFO @@ -1,6 +1,6 @@ Metadata-Version: 1.0 Name: vobject -Version: 0.6.6 +Version: 0.7.1 Summary: VObject: module for reading vCard and vCalendar files Home-page: http://vobject.skyhouseconsulting.com Author: Jeffrey Harris @@ -18,22 +18,28 @@ Description: Description Recent changes -------------- - - * Allow unicode names for TZIDs - * Worked around Lotus Notes use of underscores in names by just silently replacing + - Added change_tz module and script for quickly changing event timezones for an + ics file. Requires PyICU. + - Add support for BYMONTHDAY=-1 (days before the end of the month) when setting rrules + from a dateutil rrule + - Tolerate a Ruby iCalendar library escaping semi-colons in RRULEs + - Make vobjects pickle-able + - Add introspection help for IPython so tab completion works with + vobject's custom __getattr__ + - Allow Outlook's technically illegal use of commas in TZIDs + - Allow unicode names for TZIDs + - Worked around Lotus Notes use of underscores in names by just silently replacing with dashes - * When allowing quoted-printable data, honor CHARSET for each line, defaulting to + - When allowing quoted-printable data, honor CHARSET for each line, defaulting to iso-8859-1 - * Simplified directory layout, unit tests are now available via setup.py test - * Added VAVAILABILITY support - * Improved wrapping of unicode lines, serialize encodes unicode as utf-8 by default + - Simplified directory layout, unit tests are now available via setup.py test For older changes, see - * http://vobject.skyhouseconsulting.com/history.html or - * http://websvn.osafoundation.org/listing.php?repname=vobject&path=/trunk/ + - http://vobject.skyhouseconsulting.com/history.html or + - http://websvn.osafoundation.org/listing.php?repname=vobject&path=/trunk/ Platform: any -Classifier: Development Status :: 4 - Beta +Classifier: Development Status :: 5 - Production/Stable Classifier: Environment :: Console Classifier: License :: OSI Approved :: BSD License Classifier: Intended Audience :: Developers diff --git a/setup.py b/setup.py index bf3c141..1c70429 100755 --- a/setup.py +++ b/setup.py @@ -12,19 +12,25 @@ Requires python 2.4 or later, dateutil (http://labix.org/python-dateutil) 1.1 or Recent changes -------------- - - * Allow unicode names for TZIDs - * Worked around Lotus Notes use of underscores in names by just silently replacing + - Added change_tz module and script for quickly changing event timezones for an + ics file. Requires PyICU. + - Add support for BYMONTHDAY=-1 (days before the end of the month) when setting rrules + from a dateutil rrule + - Tolerate a Ruby iCalendar library escaping semi-colons in RRULEs + - Make vobjects pickle-able + - Add introspection help for IPython so tab completion works with + vobject's custom __getattr__ + - Allow Outlook's technically illegal use of commas in TZIDs + - Allow unicode names for TZIDs + - Worked around Lotus Notes use of underscores in names by just silently replacing with dashes - * When allowing quoted-printable data, honor CHARSET for each line, defaulting to + - When allowing quoted-printable data, honor CHARSET for each line, defaulting to iso-8859-1 - * Simplified directory layout, unit tests are now available via setup.py test - * Added VAVAILABILITY support - * Improved wrapping of unicode lines, serialize encodes unicode as utf-8 by default + - Simplified directory layout, unit tests are now available via setup.py test -For older changes, see - * http://vobject.skyhouseconsulting.com/history.html or - * http://websvn.osafoundation.org/listing.php?repname=vobject&path=/trunk/ +For older changes, see + - http://vobject.skyhouseconsulting.com/history.html or + - http://websvn.osafoundation.org/listing.php?repname=vobject&path=/trunk/ """ @@ -36,13 +42,14 @@ from setuptools import setup, find_packages doclines = __doc__.splitlines() setup(name = "vobject", - version = "0.6.6", + version = "0.7.1", author = "Jeffrey Harris", author_email = "jeffrey@osafoundation.org", license = "Apache", zip_safe = True, url = "http://vobject.skyhouseconsulting.com", - entry_points = { 'console_scripts': ['ics_diff = vobject.ics_diff:main'] }, + entry_points = { 'console_scripts': ['ics_diff = vobject.ics_diff:main', + 'change_tz = vobject.change_tz:main']}, include_package_data = True, test_suite = "test_vobject", @@ -53,7 +60,7 @@ setup(name = "vobject", description = doclines[0], long_description = "\n".join(doclines[2:]), classifiers = """ - Development Status :: 4 - Beta + Development Status :: 5 - Production/Stable Environment :: Console License :: OSI Approved :: BSD License Intended Audience :: Developers diff --git a/test_files/more_tests.txt b/test_files/more_tests.txt index 8f4ec9a..a19cac1 100644 --- a/test_files/more_tests.txt +++ b/test_files/more_tests.txt @@ -19,17 +19,31 @@ FN:Helloሴ World! N:World;Helloሴ;;; END:VCARD -Unicode in TZID +Helper function ............... - >>> from pkg_resources import resource_stream ->>> f = resource_stream(__name__, 'test_files/tzid_8bit.ics') +>>> def get_stream(path): +... try: +... return resource_stream(__name__, 'test_files/' + path) +... except: # different paths, depending on whether doctest is run directly +... return resource_stream(__name__, path) + +Unicode in TZID +............... +>>> f = get_stream("tzid_8bit.ics") >>> cal = vobject.readOne(f) >>> print cal.vevent.dtstart.value 2008-05-30 15:00:00+06:00 >>> print cal.vevent.dtstart.serialize() DTSTART;TZID=Екатеринбург:20080530T150000 +Commas in TZID +.............. +>>> f = get_stream("ms_tzid.ics") +>>> cal = vobject.readOne(f) +>>> print cal.vevent.dtstart.value +2008-05-30 15:00:00+10:00 + Equality in vCards .................. @@ -45,6 +59,14 @@ Organization (org) >>> print card.org.serialize() ORG:Company\, Inc.;main unit;sub-unit +Ruby escapes semi-colons in rrules +.................................. + +>>> f = get_stream("ruby_rrule.ics") +>>> cal = vobject.readOne(f) +>>> iter(cal.vevent.rruleset).next() +datetime.datetime(2003, 1, 1, 7, 0) + quoted-printable ................ diff --git a/test_files/ms_tzid.ics b/test_files/ms_tzid.ics new file mode 100644 index 0000000..0db2c5c --- /dev/null +++ b/test_files/ms_tzid.ics @@ -0,0 +1,39 @@ +BEGIN:VCALENDAR +PRODID:-//Microsoft Corporation//Outlook 12.0 MIMEDIR//EN +VERSION:2.0 +BEGIN:VTIMEZONE +TZID:Canberra, Melbourne, Sydney +BEGIN:STANDARD +DTSTART:20010325T020000 +RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=-1SU;BYMONTH=3;UNTIL=20050327T070000Z +TZOFFSETFROM:+1100 +TZOFFSETTO:+1000 +TZNAME:Standard Time +END:STANDARD +BEGIN:STANDARD +DTSTART:20060402T020000 +RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=1SU;BYMONTH=4;UNTIL=20060402T070000Z +TZOFFSETFROM:+1100 +TZOFFSETTO:+1000 +TZNAME:Standard Time +END:STANDARD +BEGIN:STANDARD +DTSTART:20070325T020000 +RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=-1SU;BYMONTH=3 +TZOFFSETFROM:+1100 +TZOFFSETTO:+1000 +TZNAME:Standard Time +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:20001029T020000 +RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:+1000 +TZOFFSETTO:+1100 +TZNAME:Daylight Savings Time +END:DAYLIGHT +END:VTIMEZONE +BEGIN:VEVENT +UID:CommaTest +DTSTART;TZID="Canberra, Melbourne, Sydney":20080530T150000 +END:VEVENT +END:VCALENDAR \ No newline at end of file diff --git a/test_files/ruby_rrule.ics b/test_files/ruby_rrule.ics new file mode 100644 index 0000000..6999513 --- /dev/null +++ b/test_files/ruby_rrule.ics @@ -0,0 +1,16 @@ +BEGIN:VCALENDAR +VERSION:2.0 +CALSCALE:GREGORIAN +METHOD:PUBLISH +PRODID:-//LinkeSOFT GmbH//NONSGML DIMEX//EN +BEGIN:VEVENT +SEQUENCE:0 +RRULE:FREQ=DAILY\;COUNT=10 +DTEND:20030101T080000 +UID:2008-05-29T17:31:42+02:00_865561242 +CATEGORIES:Unfiled +SUMMARY:Something +DTSTART:20030101T070000 +DTSTAMP:20080529T152100 +END:VEVENT +END:VCALENDAR \ No newline at end of file diff --git a/test_vobject.py b/test_vobject.py index 1ed3858..b0dfefa 100644 --- a/test_vobject.py +++ b/test_vobject.py @@ -13,7 +13,7 @@ base.logger.setLevel(base.logging.FATAL) # named additional_tests for setuptools def additional_tests(): - flags = doctest.NORMALIZE_WHITESPACE | doctest.REPORT_ONLY_FIRST_FAILURE + flags = doctest.NORMALIZE_WHITESPACE | doctest.REPORT_ONLY_FIRST_FAILURE | doctest.ELLIPSIS suite = unittest.TestSuite() for module in base, test_vobject, icalendar, vobject, vcard: suite.addTest(doctest.DocTestSuite(module, optionflags=flags)) @@ -531,7 +531,7 @@ __test__ = { "Test readOne" : "Serializing with timezones test" : """ - >>> from dateutil.rrule import rrule, rruleset, WEEKLY + >>> from dateutil.rrule import rrule, rruleset, WEEKLY, MONTHLY >>> pacific = dateutil.tz.tzical(StringIO.StringIO(timezones)).get('US/Pacific') >>> cal = base.Component('VCALENDAR') >>> cal.setBehavior(icalendar.VCalendar2_0) @@ -539,9 +539,9 @@ __test__ = { "Test readOne" : >>> ev.add('dtstart').value = datetime.datetime(2005, 10, 12, 9, tzinfo = pacific) >>> set = rruleset() >>> set.rrule(rrule(WEEKLY, interval=2, byweekday=[2,4], until=datetime.datetime(2005, 12, 15, 9))) + >>> set.rrule(rrule(MONTHLY, bymonthday=[-1,-5])) >>> set.exdate(datetime.datetime(2005, 10, 14, 9, tzinfo = pacific)) >>> ev.rruleset = set - >>> ev.add('uid').value = "uid could be generated but doctest complains" >>> ev.add('duration').value = datetime.timedelta(hours=1) >>> print cal.serialize() BEGIN:VCALENDAR @@ -565,11 +565,12 @@ __test__ = { "Test readOne" : END:DAYLIGHT END:VTIMEZONE BEGIN:VEVENT - UID:uid could be generated but doctest complains + UID:... DTSTART;TZID=US/Pacific:20051012T090000 DURATION:PT1H EXDATE;TZID=US/Pacific:20051014T090000 RRULE:FREQ=WEEKLY;BYDAY=WE,FR;INTERVAL=2;UNTIL=20051215T090000 + RRULE:FREQ=MONTHLY;BYMONTHDAY=-1,-5 END:VEVENT END:VCALENDAR >>> apple = dateutil.tz.tzical(StringIO.StringIO(timezones)).get('America/Montreal') @@ -620,11 +621,12 @@ __test__ = { "Test readOne" : END:DAYLIGHT END:VTIMEZONE BEGIN:VEVENT - UID:uid could be generated but doctest complains + UID:... DTSTART;TZID=America/Montreal:20051012T090000 DURATION:PT1H EXDATE;TZID=US/Pacific:20051014T090000 RRULE:FREQ=WEEKLY;BYDAY=WE,FR;INTERVAL=2;UNTIL=20051215T090000 + RRULE:FREQ=MONTHLY;BYMONTHDAY=-1,-5 END:VEVENT END:VCALENDAR """, diff --git a/vobject.egg-info/PKG-INFO b/vobject.egg-info/PKG-INFO index 3353477..2d7c261 100644 --- a/vobject.egg-info/PKG-INFO +++ b/vobject.egg-info/PKG-INFO @@ -1,6 +1,6 @@ Metadata-Version: 1.0 Name: vobject -Version: 0.6.6 +Version: 0.7.1 Summary: VObject: module for reading vCard and vCalendar files Home-page: http://vobject.skyhouseconsulting.com Author: Jeffrey Harris @@ -18,22 +18,28 @@ Description: Description Recent changes -------------- - - * Allow unicode names for TZIDs - * Worked around Lotus Notes use of underscores in names by just silently replacing + - Added change_tz module and script for quickly changing event timezones for an + ics file. Requires PyICU. + - Add support for BYMONTHDAY=-1 (days before the end of the month) when setting rrules + from a dateutil rrule + - Tolerate a Ruby iCalendar library escaping semi-colons in RRULEs + - Make vobjects pickle-able + - Add introspection help for IPython so tab completion works with + vobject's custom __getattr__ + - Allow Outlook's technically illegal use of commas in TZIDs + - Allow unicode names for TZIDs + - Worked around Lotus Notes use of underscores in names by just silently replacing with dashes - * When allowing quoted-printable data, honor CHARSET for each line, defaulting to + - When allowing quoted-printable data, honor CHARSET for each line, defaulting to iso-8859-1 - * Simplified directory layout, unit tests are now available via setup.py test - * Added VAVAILABILITY support - * Improved wrapping of unicode lines, serialize encodes unicode as utf-8 by default + - Simplified directory layout, unit tests are now available via setup.py test For older changes, see - * http://vobject.skyhouseconsulting.com/history.html or - * http://websvn.osafoundation.org/listing.php?repname=vobject&path=/trunk/ + - http://vobject.skyhouseconsulting.com/history.html or + - http://websvn.osafoundation.org/listing.php?repname=vobject&path=/trunk/ Platform: any -Classifier: Development Status :: 4 - Beta +Classifier: Development Status :: 5 - Production/Stable Classifier: Environment :: Console Classifier: License :: OSI Approved :: BSD License Classifier: Intended Audience :: Developers diff --git a/vobject.egg-info/SOURCES.txt b/vobject.egg-info/SOURCES.txt index 721a609..f92917f 100644 --- a/vobject.egg-info/SOURCES.txt +++ b/vobject.egg-info/SOURCES.txt @@ -6,12 +6,15 @@ test_vobject.py ez_setup/README.txt ez_setup/__init__.py test_files/more_tests.txt +test_files/ms_tzid.ics test_files/recurrence.ics +test_files/ruby_rrule.ics test_files/tzid_8bit.ics test_files/utf8_test.ics vobject/__init__.py vobject/base.py vobject/behavior.py +vobject/change_tz.py vobject/hcalendar.py vobject/icalendar.py vobject/ics_diff.py diff --git a/vobject.egg-info/entry_points.txt b/vobject.egg-info/entry_points.txt index 405bd65..d076c85 100644 --- a/vobject.egg-info/entry_points.txt +++ b/vobject.egg-info/entry_points.txt @@ -1,3 +1,4 @@ [console_scripts] +change_tz = vobject.change_tz:main ics_diff = vobject.ics_diff:main diff --git a/vobject/base.py b/vobject/base.py index 107e71c..f641325 100644 --- a/vobject/base.py +++ b/vobject/base.py @@ -293,6 +293,19 @@ class ContentLine(VBase): except: return False + def _getAttributeNames(self): + """Return a list of attributes of the object. + + Python 2.6 will add __dir__ to customize what attributes are returned + by dir, for now copy PyCrust so that IPython can accurately do + completion. + + """ + keys = self.params.keys() + params = [param + '_param' for param in keys] + params.extend(param + '_paramlist' for param in keys) + return params + def __getattr__(self, name): """Make params accessible via self.foo_param or self.foo_paramlist. @@ -430,6 +443,18 @@ class Component(VBase): raise VObjectError("This component already has a PROFILE or uses BEGIN.") self.name = name.upper() + def _getAttributeNames(self): + """Return a list of attributes of the object. + + Python 2.6 will add __dir__ to customize what attributes are returned + by dir, for now copy PyCrust so that IPython can accurately do + completion. + + """ + names = self.contents.keys() + names.extend(name + '_list' for name in self.contents.keys()) + return names + def __getattr__(self, name): """For convenience, make self.contents directly accessible. @@ -437,6 +462,10 @@ class Component(VBase): which are legal in IANA tokens. """ + # if the object is being re-created by pickle, self.contents may not + # be set, don't get into an infinite loop over the issue + if name == 'contents': + return object.__getattribute__(self, name) try: if name.endswith('_list'): return self.contents[toVName(name, 5)] diff --git a/vobject/change_tz.py b/vobject/change_tz.py new file mode 100644 index 0000000..1227676 --- /dev/null +++ b/vobject/change_tz.py @@ -0,0 +1,77 @@ +"""Translate an ics file's events to a different timezone.""" + +from optparse import OptionParser +import icalendar, base +import sys +try: + import PyICU +except: + PyICU = None + +from datetime import datetime + +def change_tz(cal, new_timezone, default, utc_only=False, utc_tz=icalendar.utc): + for vevent in getattr(cal, 'vevent_list', []): + start = getattr(vevent, 'dtstart', None) + end = getattr(vevent, 'dtend', None) + for node in (start, end): + if node: + dt = node.value + if (isinstance(dt, datetime) and + (not utc_only or dt.tzinfo == utc_tz)): + if dt.tzinfo is None: + dt = dt.replace(tzinfo = default) + node.value = dt.astimezone(new_timezone) + +def main(): + options, args = get_options() + if PyICU is None: + print "Failure. change_tz requires PyICU, exiting" + elif options.list: + for tz_string in PyICU.TimeZone.createEnumeration(): + print tz_string + elif args: + utc_only = options.utc + print "Converting %s events" % "only UTC" if utc_only else "all" + ics_file = args[0] + timezone = PyICU.ICUtzinfo.getInstance(args[1]) if len(args) > 1 else PyICU.ICUtzinfo.default + print "... Reading %s" % ics_file + cal = base.readOne(file(ics_file)) + change_tz(cal, timezone, PyICU.ICUtzinfo.default, utc_only) + + out_name = ics_file + '.converted' + print "... Writing %s" % out_name + out = file(out_name, 'wb') + cal.serialize(out) + print "Done" + + +version = "0.1" + +def get_options(): + ##### Configuration options ##### + + usage = """usage: %prog [options] ics_file [timezone]""" + parser = OptionParser(usage=usage, version=version) + parser.set_description("change_tz will convert the timezones in an ics file. ") + + parser.add_option("-u", "--only-utc", dest="utc", action="store_true", + default=False, help="Only change UTC events.") + parser.add_option("-l", "--list", dest="list", action="store_true", + default=False, help="List available timezones") + + + (cmdline_options, args) = parser.parse_args() + if not args and not cmdline_options.list: + print "error: too few arguments given" + print + print parser.format_help() + return False, False + + return cmdline_options, args + +if __name__ == "__main__": + try: + main() + except KeyboardInterrupt: + print "Aborted" diff --git a/vobject/icalendar.py b/vobject/icalendar.py index 09862c1..4c4f49b 100644 --- a/vobject/icalendar.py +++ b/vobject/icalendar.py @@ -401,8 +401,10 @@ class RecurringComponent(Component): return None # rrulestr complains about unicode, so cast to str - rule = dateutil.rrule.rrulestr(str(line.value), - dtstart=dtstart) + # a Ruby iCalendar library escapes semi-colons in rrules, + # so also remove any backslashes + value = str(line.value).replace('\\', '') + rule = dateutil.rrule.rrulestr(value, dtstart=dtstart) until = rule._until if until is not None and \ isinstance(dtstart, datetime.datetime) and \ @@ -525,6 +527,9 @@ class RecurringComponent(Component): # ignore bymonthday if it's generated by dateutil values['BYMONTHDAY'] = [str(n) for n in rule._bymonthday] + if rule._bynmonthday is not None and len(rule._bynmonthday) > 0: + values.setdefault('BYMONTHDAY', []).extend(str(n) for n in rule._bynmonthday) + if rule._bymonth is not None and len(rule._bymonth) > 0: if (rule._byweekday is not None or len(rule._bynweekday or ()) > 0 or @@ -894,10 +899,19 @@ class VTimezone(VCalendarComponentBehavior): @staticmethod def transformFromNative(obj): return obj - - registerBehavior(VTimezone) +class TZID(behavior.Behavior): + """Don't use TextBehavior for TZID. + + RFC2445 only allows TZID lines to be paramtext, so they shouldn't need any + encoding or decoding. Unfortunately, some Microsoft products use commas + in TZIDs which should NOT be treated as a multi-valued text property, nor + do we want to escape them. Leaving them alone works for Microsoft's breakage, + and doesn't affect compliant iCalendar streams. + """ +registerBehavior(TZID) + class DaylightOrStandard(VCalendarComponentBehavior): hasNative = False knownChildren = {'DTSTART': (1, 1, None),#min, max, behaviorRegistry id @@ -1467,7 +1481,7 @@ registerBehavior(MultiDateBehavior, 'EXDATE') textList = ['CALSCALE', 'METHOD', 'PRODID', 'CLASS', 'COMMENT', 'DESCRIPTION', 'LOCATION', 'STATUS', 'SUMMARY', 'TRANSP', 'CONTACT', 'RELATED-TO', - 'UID', 'ACTION', 'REQUEST-STATUS', 'TZID', 'BUSYTYPE'] + 'UID', 'ACTION', 'REQUEST-STATUS', 'BUSYTYPE'] map(lambda x: registerBehavior(TextBehavior, x), textList) multiTextList = ['CATEGORIES', 'RESOURCES'] -- cgit v1.2.3