diff options
author | Guido Guenther <agx@sigxcpu.org> | 2008-08-08 12:12:58 +0200 |
---|---|---|
committer | Guido Guenther <agx@sigxcpu.org> | 2008-08-08 12:12:58 +0200 |
commit | 3fc74490630c3277b0d8c107f7f3bc51176486a4 (patch) | |
tree | 9815b720afce5d2e3c762ac35ddb627f91e5d141 | |
parent | 6112f2272bcb3a9accc151061d30d62f3046b23a (diff) | |
parent | 43d36676eb8312d12aa50f124fd5f49902db10dd (diff) |
Merge commit 'upstream/0.7.1'
-rw-r--r-- | PKG-INFO | 28 | ||||
-rwxr-xr-x | setup.py | 33 | ||||
-rw-r--r-- | test_files/more_tests.txt | 28 | ||||
-rw-r--r-- | test_files/ms_tzid.ics | 39 | ||||
-rw-r--r-- | test_files/ruby_rrule.ics | 16 | ||||
-rw-r--r-- | test_vobject.py | 12 | ||||
-rw-r--r-- | vobject.egg-info/PKG-INFO | 28 | ||||
-rw-r--r-- | vobject.egg-info/SOURCES.txt | 3 | ||||
-rw-r--r-- | vobject.egg-info/entry_points.txt | 1 | ||||
-rw-r--r-- | vobject/base.py | 29 | ||||
-rw-r--r-- | vobject/change_tz.py | 77 | ||||
-rw-r--r-- | vobject/icalendar.py | 24 |
12 files changed, 270 insertions, 48 deletions
@@ -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 @@ -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'] |