summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGuido Guenther <agx@sigxcpu.org>2008-08-08 12:12:58 +0200
committerGuido Guenther <agx@sigxcpu.org>2008-08-08 12:12:58 +0200
commit3fc74490630c3277b0d8c107f7f3bc51176486a4 (patch)
tree9815b720afce5d2e3c762ac35ddb627f91e5d141
parent6112f2272bcb3a9accc151061d30d62f3046b23a (diff)
parent43d36676eb8312d12aa50f124fd5f49902db10dd (diff)
Merge commit 'upstream/0.7.1'
-rw-r--r--PKG-INFO28
-rwxr-xr-xsetup.py33
-rw-r--r--test_files/more_tests.txt28
-rw-r--r--test_files/ms_tzid.ics39
-rw-r--r--test_files/ruby_rrule.ics16
-rw-r--r--test_vobject.py12
-rw-r--r--vobject.egg-info/PKG-INFO28
-rw-r--r--vobject.egg-info/SOURCES.txt3
-rw-r--r--vobject.egg-info/entry_points.txt1
-rw-r--r--vobject/base.py29
-rw-r--r--vobject/change_tz.py77
-rw-r--r--vobject/icalendar.py24
12 files changed, 270 insertions, 48 deletions
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']