summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--PKG-INFO28
-rwxr-xr-xsetup.py44
-rw-r--r--src/vobject.egg-info/PKG-INFO27
-rw-r--r--src/vobject.egg-info/SOURCES.txt25
-rw-r--r--test_files/more_tests.txt62
-rw-r--r--test_files/recurrence.ics (renamed from tests/recurrence.ics)0
-rw-r--r--test_files/tzid_8bit.ics23
-rw-r--r--test_files/utf8_test.ics (renamed from tests/utf8_test.ics)0
-rw-r--r--test_vobject.py (renamed from tests/tests.py)56
-rw-r--r--tests/more_tests.txt35
-rw-r--r--vobject.egg-info/PKG-INFO43
-rw-r--r--vobject.egg-info/SOURCES.txt26
-rw-r--r--vobject.egg-info/dependency_links.txt (renamed from src/vobject.egg-info/dependency_links.txt)0
-rw-r--r--vobject.egg-info/entry_points.txt (renamed from src/vobject.egg-info/entry_points.txt)0
-rw-r--r--vobject.egg-info/requires.txt (renamed from src/vobject.egg-info/requires.txt)0
-rw-r--r--vobject.egg-info/top_level.txt (renamed from src/vobject.egg-info/top_level.txt)0
-rw-r--r--vobject.egg-info/zip-safe (renamed from src/vobject.egg-info/zip-safe)0
-rw-r--r--vobject/__init__.py (renamed from src/vobject/__init__.py)0
-rw-r--r--vobject/base.py (renamed from src/vobject/base.py)58
-rw-r--r--vobject/behavior.py (renamed from src/vobject/behavior.py)0
-rw-r--r--vobject/hcalendar.py (renamed from src/vobject/hcalendar.py)0
-rw-r--r--vobject/icalendar.py (renamed from src/vobject/icalendar.py)45
-rw-r--r--vobject/ics_diff.py (renamed from src/vobject/ics_diff.py)0
-rw-r--r--vobject/vcard.py (renamed from src/vobject/vcard.py)0
-rw-r--r--vobject/win32tz.py (renamed from src/vobject/win32tz.py)0
25 files changed, 291 insertions, 181 deletions
diff --git a/PKG-INFO b/PKG-INFO
index 6ecafcc..3353477 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,20 +1,36 @@
Metadata-Version: 1.0
Name: vobject
-Version: 0.6.0
+Version: 0.6.6
Summary: VObject: module for reading vCard and vCalendar files
Home-page: http://vobject.skyhouseconsulting.com
Author: Jeffrey Harris
Author-email: jeffrey@osafoundation.org
License: Apache
-Description: Parses iCalendar and vCard files into Python data structures, decoding the relevant encodings. Also serializes vobject data structures to iCalendar, vCard, or (expirementally) hCalendar unicode strings.
+Description: Description
+ -----------
+
+ Parses iCalendar and vCard files into Python data structures, decoding the relevant encodings. Also serializes vobject data structures to iCalendar, vCard, or (expirementally) hCalendar unicode strings.
+
+ Requirements
+ ------------
Requires python 2.4 or later, dateutil (http://labix.org/python-dateutil) 1.1 or later.
- Recent changes:
- - Added VAVAILABILITY support
- - Improved wrapping of unicode lines, serialize encodes unicode as utf-8 by default
+ Recent changes
+ --------------
+
+ * 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
+ 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
- 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/
Platform: any
Classifier: Development Status :: 4 - Beta
diff --git a/setup.py b/setup.py
index 2516b3e..bf3c141 100755
--- a/setup.py
+++ b/setup.py
@@ -1,14 +1,30 @@
"""VObject: module for reading vCard and vCalendar files
+Description
+-----------
+
Parses iCalendar and vCard files into Python data structures, decoding the relevant encodings. Also serializes vobject data structures to iCalendar, vCard, or (expirementally) hCalendar unicode strings.
+Requirements
+------------
+
Requires python 2.4 or later, dateutil (http://labix.org/python-dateutil) 1.1 or later.
-Recent changes:
-- Added VAVAILABILITY support
-- Improved wrapping of unicode lines, serialize encodes unicode as utf-8 by default
+Recent changes
+--------------
-For older changes, see http://vobject.skyhouseconsulting.com/history.html or http://websvn.osafoundation.org/listing.php?repname=vobject&path=/trunk/
+ * 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
+ 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
+
+For older changes, see
+ * http://vobject.skyhouseconsulting.com/history.html or
+ * http://websvn.osafoundation.org/listing.php?repname=vobject&path=/trunk/
"""
@@ -17,33 +33,23 @@ use_setuptools()
from setuptools import setup, find_packages
-#from distutils.core import setup
-
-# Metadata
-PACKAGE_NAME = "vobject"
-PACKAGE_VERSION = "0.6.0"
-
-ALL_EXTS = ['*.py', '*.ics', '*.txt']
-
-packages = ['vobject']
-
doclines = __doc__.splitlines()
setup(name = "vobject",
- version = PACKAGE_VERSION,
+ version = "0.6.6",
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'] },
- package_dir = {'':'src'},
- package_data = {'': ALL_EXTS},
+ include_package_data = True,
+ test_suite = "test_vobject",
install_requires = ['python-dateutil >= 1.1'],
-
+
platforms = ["any"],
- packages = ["vobject"],
+ packages = find_packages(),
description = doclines[0],
long_description = "\n".join(doclines[2:]),
classifiers = """
diff --git a/src/vobject.egg-info/PKG-INFO b/src/vobject.egg-info/PKG-INFO
deleted file mode 100644
index 6ecafcc..0000000
--- a/src/vobject.egg-info/PKG-INFO
+++ /dev/null
@@ -1,27 +0,0 @@
-Metadata-Version: 1.0
-Name: vobject
-Version: 0.6.0
-Summary: VObject: module for reading vCard and vCalendar files
-Home-page: http://vobject.skyhouseconsulting.com
-Author: Jeffrey Harris
-Author-email: jeffrey@osafoundation.org
-License: Apache
-Description: Parses iCalendar and vCard files into Python data structures, decoding the relevant encodings. Also serializes vobject data structures to iCalendar, vCard, or (expirementally) hCalendar unicode strings.
-
- Requires python 2.4 or later, dateutil (http://labix.org/python-dateutil) 1.1 or later.
-
- Recent changes:
- - Added VAVAILABILITY support
- - Improved wrapping of unicode lines, serialize encodes unicode as utf-8 by default
-
- For older changes, see 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: Environment :: Console
-Classifier: License :: OSI Approved :: BSD License
-Classifier: Intended Audience :: Developers
-Classifier: Natural Language :: English
-Classifier: Programming Language :: Python
-Classifier: Operating System :: OS Independent
-Classifier: Topic :: Text Processing
diff --git a/src/vobject.egg-info/SOURCES.txt b/src/vobject.egg-info/SOURCES.txt
deleted file mode 100644
index 8e378ac..0000000
--- a/src/vobject.egg-info/SOURCES.txt
+++ /dev/null
@@ -1,25 +0,0 @@
-ACKNOWLEDGEMENTS.txt
-LICENSE.txt
-README.txt
-setup.py
-ez_setup/README.txt
-ez_setup/__init__.py
-src/vobject/__init__.py
-src/vobject/base.py
-src/vobject/behavior.py
-src/vobject/hcalendar.py
-src/vobject/icalendar.py
-src/vobject/ics_diff.py
-src/vobject/vcard.py
-src/vobject/win32tz.py
-src/vobject.egg-info/PKG-INFO
-src/vobject.egg-info/SOURCES.txt
-src/vobject.egg-info/dependency_links.txt
-src/vobject.egg-info/entry_points.txt
-src/vobject.egg-info/requires.txt
-src/vobject.egg-info/top_level.txt
-src/vobject.egg-info/zip-safe
-tests/more_tests.txt
-tests/recurrence.ics
-tests/tests.py
-tests/utf8_test.ics \ No newline at end of file
diff --git a/test_files/more_tests.txt b/test_files/more_tests.txt
new file mode 100644
index 0000000..8f4ec9a
--- /dev/null
+++ b/test_files/more_tests.txt
@@ -0,0 +1,62 @@
+
+Unicode in vCards
+.................
+
+>>> import vobject
+>>> card = vobject.vCard()
+>>> card.add('fn').value = u'Hello\u1234 World!'
+>>> card.add('n').value = vobject.vcard.Name('World', u'Hello\u1234')
+>>> card.add('adr').value = vobject.vcard.Address(u'5\u1234 Nowhere, Apt 1', 'Berkeley', 'CA', '94704', 'USA')
+>>> card
+<VCARD| [<ADR{}5? Nowhere, Apt 1\nBerkeley, CA 94704\nUSA>, <FN{}Hello? World!>, <N{} Hello? World >]>
+>>> card.serialize().decode("utf-8")
+u'BEGIN:VCARD\r\nVERSION:3.0\r\nADR:;;5\u1234 Nowhere\\, Apt 1;Berkeley;CA;94704;USA\r\nFN:Hello\u1234 World!\r\nN:World;Hello\u1234;;;\r\nEND:VCARD\r\n'
+>>> print card.serialize()
+BEGIN:VCARD
+VERSION:3.0
+ADR:;;5ሴ Nowhere\, Apt 1;Berkeley;CA;94704;USA
+FN:Helloሴ World!
+N:World;Helloሴ;;;
+END:VCARD
+
+Unicode in TZID
+...............
+
+>>> from pkg_resources import resource_stream
+>>> f = resource_stream(__name__, 'test_files/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
+
+Equality in vCards
+..................
+
+>>> card.adr.value == vobject.vcard.Address('Just a street')
+False
+>>> card.adr.value == vobject.vcard.Address(u'5\u1234 Nowhere, Apt 1', 'Berkeley', 'CA', '94704', 'USA')
+True
+
+Organization (org)
+..................
+
+>>> card.add('org').value = ["Company, Inc.", "main unit", "sub-unit"]
+>>> print card.org.serialize()
+ORG:Company\, Inc.;main unit;sub-unit
+
+
+quoted-printable
+................
+
+>>> vcf = 'BEGIN:VCARD\nVERSION:2.1\nN;ENCODING=QUOTED-PRINTABLE:;=E9\nFN;ENCODING=QUOTED-PRINTABLE:=E9\nTEL;HOME:0111111111\nEND:VCARD\n\n'
+>>> vcf = vobject.readOne(vcf)
+>>> vcf.n.value
+u';\xe9'
+>>> vcf.serialize()
+'BEGIN:VCARD\r\nFN:\xc3\xa9\r\nN:;\xc3\xa9\r\nTEL:0111111111\r\nVERSION:2.1\r\nEND:VCARD\r\n'
+
+>>> vcs = 'BEGIN:VCALENDAR\r\nPRODID:-//OpenSync//NONSGML OpenSync vformat 0.3//EN\r\nVERSION:1.0\r\nBEGIN:VEVENT\r\nDESCRIPTION;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:foo =C3=A5=0Abar =C3=A4=\r\n=0Abaz =C3=B6\r\nUID:20080406T152030Z-7822\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n'
+>>> vcs = vobject.readOne(vcs, allowQP = True)
+>>> vcs.serialize()
+'BEGIN:VCALENDAR\r\nPRODID:-//OpenSync//NONSGML OpenSync vformat 0.3//EN\r\nVERSION:1.0\r\nBEGIN:VEVENT\r\nDESCRIPTION:foo \xc3\xa5\nbar \xc3\xa4\nbaz \xc3\xb6\r\nUID:20080406T152030Z-7822\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n'
diff --git a/tests/recurrence.ics b/test_files/recurrence.ics
index f592234..f592234 100644
--- a/tests/recurrence.ics
+++ b/test_files/recurrence.ics
diff --git a/test_files/tzid_8bit.ics b/test_files/tzid_8bit.ics
new file mode 100644
index 0000000..91d35d2
--- /dev/null
+++ b/test_files/tzid_8bit.ics
@@ -0,0 +1,23 @@
+BEGIN:VCALENDAR
+PRODID:-//Microsoft Corporation//Outlook 12.0 MIMEDIR//EN
+VERSION:2.0
+BEGIN:VTIMEZONE
+TZID:Екатеринбург
+BEGIN:STANDARD
+DTSTART:16011028T030000
+RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
+TZOFFSETFROM:+0600
+TZOFFSETTO:+0500
+END:STANDARD
+BEGIN:DAYLIGHT
+DTSTART:16010325T020000
+RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=3
+TZOFFSETFROM:+0500
+TZOFFSETTO:+0600
+END:DAYLIGHT
+END:VTIMEZONE
+BEGIN:VEVENT
+UID:CyrillicTest
+DTSTART;TZID=Екатеринбург:20080530T150000
+END:VEVENT
+END:VCALENDAR
diff --git a/tests/utf8_test.ics b/test_files/utf8_test.ics
index fbb9ada..fbb9ada 100644
--- a/tests/utf8_test.ics
+++ b/test_files/utf8_test.ics
diff --git a/tests/tests.py b/test_vobject.py
index b36793b..1ed3858 100644
--- a/tests/tests.py
+++ b/test_vobject.py
@@ -1,31 +1,32 @@
"""Long or boring tests for vobjects."""
-# add source directory to front of sys path
-import sys, os
-basepath = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
-sys.path.insert( 0, os.path.join( basepath, 'src', 'vobject' ) )
-
-import base, icalendar, behavior, vcard, hcalendar
+import vobject
+from vobject import base, icalendar, behavior, vcard, hcalendar
import StringIO, re, dateutil.tz, datetime
+import doctest, test_vobject, unittest
+
+from pkg_resources import resource_stream
+
base.logger.setLevel(base.logging.FATAL)
#------------------- Testing and running functions -----------------------------
-def _test():
- import doctest, base, tests, icalendar, __init__, re
+# named additional_tests for setuptools
+def additional_tests():
+
flags = doctest.NORMALIZE_WHITESPACE | doctest.REPORT_ONLY_FIRST_FAILURE
- for mod in base, tests, icalendar, __init__, vcard:
- doctest.testmod(mod, verbose=0, optionflags=flags)
- doctest.testfile('more_tests.txt', optionflags=flags)
- try:
- sys.path.pop()
- sys.path.insert( 0, os.path.join( basepath, 'src' ) )
- doctest.testfile('../README.txt', optionflags=flags)
- except IOError: #allow this test to fail if we can't find README.txt
- pass
-
+ suite = unittest.TestSuite()
+ for module in base, test_vobject, icalendar, vobject, vcard:
+ suite.addTest(doctest.DocTestSuite(module, optionflags=flags))
+
+ suite.addTest(doctest.DocFileSuite(
+ 'README.txt', 'test_files/more_tests.txt',
+ package='__main__', optionflags=flags
+ ))
+ return suite
if __name__ == '__main__':
- _test()
+ runner = unittest.TextTestRunner()
+ runner.run(additional_tests())
testSilly="""
@@ -95,6 +96,7 @@ METHOD:PUBLISH
VERSION:2.0
BEGIN:VEVENT
DTSTART:19870405T020000
+X-BAD/SLASH:TRUE
X-BAD_UNDERSCORE:TRUE
UID:EC9439B1-FF65-11D6-9973-003065F99D04
END:VEVENT
@@ -360,12 +362,14 @@ __test__ = { "Test readOne" :
>>> cal = base.readOne(badLineTest)
Traceback (most recent call last):
...
- ParseError: At line 6: Failed to parse line: X-BAD_UNDERSCORE:TRUE
+ ParseError: At line 6: Failed to parse line: X-BAD/SLASH:TRUE
>>> cal = base.readOne(badLineTest, ignoreUnreadable=True)
- >>> cal.x_bad_underscore
+ >>> cal.vevent.x_bad_slash
Traceback (most recent call last):
...
- AttributeError: x_bad_underscore
+ AttributeError: x_bad_slash
+ >>> cal.vevent.x_bad_underscore
+ <X-BAD-UNDERSCORE{}TRUE>
""",
"ical trigger workaround" :
@@ -380,7 +384,7 @@ __test__ = { "Test readOne" :
"unicode test" :
r"""
- >>> f = open(os.path.join(basepath, 'tests', 'utf8_test.ics'))
+ >>> f = resource_stream(__name__, 'test_files/utf8_test.ics')
>>> vevent = base.readOne(f).vevent
>>> vevent.summary.value
u'The title \u3053\u3093\u306b\u3061\u306f\u30ad\u30c6\u30a3'
@@ -392,7 +396,7 @@ __test__ = { "Test readOne" :
# and include that day (12/28 in this test)
"recurrence test" :
r"""
- >>> f = file(os.path.join(basepath, 'tests', 'recurrence.ics'))
+ >>> f = resource_stream(__name__, 'test_files/recurrence.ics')
>>> cal = base.readOne(f)
>>> dates = list(cal.vevent.rruleset)
>>> dates[0]
@@ -638,7 +642,7 @@ __test__ = { "Test readOne" :
"""
>>> cal = base.newFromBehavior('hcalendar')
>>> cal.behavior
- <class 'hcalendar.HCalendar'>
+ <class 'vobject.hcalendar.HCalendar'>
>>> pacific = dateutil.tz.tzical(StringIO.StringIO(timezones)).get('US/Pacific')
>>> cal.add('vevent')
<VEVENT| []>
@@ -760,7 +764,7 @@ __test__ = { "Test readOne" :
>>> base.getBehavior('note') == None
True
>>> card.note.behavior
- <class 'vcard.VCardTextBehavior'>
+ <class 'vobject.vcard.VCardTextBehavior'>
>>> print card.note.value
The Mayor of the great city of Goerlitz in the great country of Germany.
Next line.
diff --git a/tests/more_tests.txt b/tests/more_tests.txt
deleted file mode 100644
index 628b091..0000000
--- a/tests/more_tests.txt
+++ /dev/null
@@ -1,35 +0,0 @@
-Unicode in vCards
-.................
-
->>> import vobject
->>> card = vobject.vCard()
->>> card.add('fn').value = u'Hello\u1234 World!'
->>> card.add('n').value = vobject.vcard.Name('World', u'Hello\u1234')
->>> card.add('adr').value = vobject.vcard.Address(u'5\u1234 Nowhere, Apt 1', 'Berkeley', 'CA', '94704', 'USA')
->>> card
-<VCARD| [<ADR{}5? Nowhere, Apt 1\nBerkeley, CA 94704\nUSA>, <FN{}Hello? World!>, <N{} Hello? World >]>
->>> card.serialize().decode("utf-8")
-u'BEGIN:VCARD\r\nVERSION:3.0\r\nADR:;;5\u1234 Nowhere\\, Apt 1;Berkeley;CA;94704;USA\r\nFN:Hello\u1234 World!\r\nN:World;Hello\u1234;;;\r\nEND:VCARD\r\n'
->>> print card.serialize().decode("utf-8").encode('ascii', 'replace')
-BEGIN:VCARD
-VERSION:3.0
-ADR:;;5? Nowhere\, Apt 1;Berkeley;CA;94704;USA
-FN:Hello? World!
-N:World;Hello?;;;
-END:VCARD
-
-
-Equality in vCards
-..................
-
->>> card.adr.value == vobject.vcard.Address('Just a street')
-False
->>> card.adr.value == vobject.vcard.Address(u'5\u1234 Nowhere, Apt 1', 'Berkeley', 'CA', '94704', 'USA')
-True
-
-Organization (org)
-..................
-
->>> card.add('org').value = ["Company, Inc.", "main unit", "sub-unit"]
->>> print card.org.serialize()
-ORG:Company\, Inc.;main unit;sub-unit \ No newline at end of file
diff --git a/vobject.egg-info/PKG-INFO b/vobject.egg-info/PKG-INFO
new file mode 100644
index 0000000..3353477
--- /dev/null
+++ b/vobject.egg-info/PKG-INFO
@@ -0,0 +1,43 @@
+Metadata-Version: 1.0
+Name: vobject
+Version: 0.6.6
+Summary: VObject: module for reading vCard and vCalendar files
+Home-page: http://vobject.skyhouseconsulting.com
+Author: Jeffrey Harris
+Author-email: jeffrey@osafoundation.org
+License: Apache
+Description: Description
+ -----------
+
+ Parses iCalendar and vCard files into Python data structures, decoding the relevant encodings. Also serializes vobject data structures to iCalendar, vCard, or (expirementally) hCalendar unicode strings.
+
+ Requirements
+ ------------
+
+ Requires python 2.4 or later, dateutil (http://labix.org/python-dateutil) 1.1 or later.
+
+ Recent changes
+ --------------
+
+ * 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
+ 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
+
+ For older changes, see
+ * 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: Environment :: Console
+Classifier: License :: OSI Approved :: BSD License
+Classifier: Intended Audience :: Developers
+Classifier: Natural Language :: English
+Classifier: Programming Language :: Python
+Classifier: Operating System :: OS Independent
+Classifier: Topic :: Text Processing
diff --git a/vobject.egg-info/SOURCES.txt b/vobject.egg-info/SOURCES.txt
new file mode 100644
index 0000000..721a609
--- /dev/null
+++ b/vobject.egg-info/SOURCES.txt
@@ -0,0 +1,26 @@
+ACKNOWLEDGEMENTS.txt
+LICENSE.txt
+README.txt
+setup.py
+test_vobject.py
+ez_setup/README.txt
+ez_setup/__init__.py
+test_files/more_tests.txt
+test_files/recurrence.ics
+test_files/tzid_8bit.ics
+test_files/utf8_test.ics
+vobject/__init__.py
+vobject/base.py
+vobject/behavior.py
+vobject/hcalendar.py
+vobject/icalendar.py
+vobject/ics_diff.py
+vobject/vcard.py
+vobject/win32tz.py
+vobject.egg-info/PKG-INFO
+vobject.egg-info/SOURCES.txt
+vobject.egg-info/dependency_links.txt
+vobject.egg-info/entry_points.txt
+vobject.egg-info/requires.txt
+vobject.egg-info/top_level.txt
+vobject.egg-info/zip-safe \ No newline at end of file
diff --git a/src/vobject.egg-info/dependency_links.txt b/vobject.egg-info/dependency_links.txt
index 8b13789..8b13789 100644
--- a/src/vobject.egg-info/dependency_links.txt
+++ b/vobject.egg-info/dependency_links.txt
diff --git a/src/vobject.egg-info/entry_points.txt b/vobject.egg-info/entry_points.txt
index 405bd65..405bd65 100644
--- a/src/vobject.egg-info/entry_points.txt
+++ b/vobject.egg-info/entry_points.txt
diff --git a/src/vobject.egg-info/requires.txt b/vobject.egg-info/requires.txt
index 21aa035..21aa035 100644
--- a/src/vobject.egg-info/requires.txt
+++ b/vobject.egg-info/requires.txt
diff --git a/src/vobject.egg-info/top_level.txt b/vobject.egg-info/top_level.txt
index 52a5040..52a5040 100644
--- a/src/vobject.egg-info/top_level.txt
+++ b/vobject.egg-info/top_level.txt
diff --git a/src/vobject.egg-info/zip-safe b/vobject.egg-info/zip-safe
index 8b13789..8b13789 100644
--- a/src/vobject.egg-info/zip-safe
+++ b/vobject.egg-info/zip-safe
diff --git a/src/vobject/__init__.py b/vobject/__init__.py
index d5daf30..d5daf30 100644
--- a/src/vobject/__init__.py
+++ b/vobject/__init__.py
diff --git a/src/vobject/base.py b/vobject/base.py
index abb6a9e..107e71c 100644
--- a/src/vobject/base.py
+++ b/vobject/base.py
@@ -4,7 +4,7 @@ import copy
import re
import sys
import logging
-import StringIO
+import StringIO, cStringIO
import string
import exceptions
import codecs
@@ -19,11 +19,11 @@ if not logging.getLogger().handlers:
logger.setLevel(logging.ERROR) # Log errors
DEBUG = False # Don't waste time on debug calls
#----------------------------------- Constants ---------------------------------
-CR = unichr(13)
-LF = unichr(10)
+CR = '\r'
+LF = '\n'
CRLF = CR + LF
-SPACE = unichr(32)
-TAB = unichr(9)
+SPACE = ' '
+TAB = '\t'
SPACEORTAB = SPACE + TAB
#-------------------------------- Useful modules -------------------------------
# use doctest, it kills two birds with one stone and docstrings often become
@@ -261,6 +261,17 @@ class ContentLine(VBase):
if qp:
self.value = str(self.value).decode('quoted-printable')
+ # self.value should be unicode for iCalendar, but if quoted-printable
+ # is used, or if the quoted-printable state machine is used, text may be
+ # encoded
+ if type(self.value) is str:
+ charset = 'iso-8859-1'
+ if 'CHARSET' in self.params:
+ charsets = self.params.pop('CHARSET')
+ if charsets:
+ charset = charsets[0]
+ self.value = unicode(self.value, charset)
+
@classmethod
def duplicate(clz, copyit):
newcopy = clz('', {}, '')
@@ -613,7 +624,9 @@ class NativeError(VObjectError):
patterns = {}
-patterns['name'] = '[a-zA-Z0-9\-]+'
+# Note that underscore is not legal for names, it's included because
+# Lotus Notes uses it
+patterns['name'] = '[a-zA-Z0-9\-_]+'
patterns['safe_char'] = '[^";:,]'
patterns['qsafe_char'] = '[^"]'
@@ -662,7 +675,7 @@ patterns['line'] = r"""
param_values_re = re.compile(patterns['param_value_grouped'], re.VERBOSE)
params_re = re.compile(patterns['params_grouped'], re.VERBOSE)
-line_re = re.compile(patterns['line'], re.VERBOSE)
+line_re = re.compile(patterns['line'], re.DOTALL | re.VERBOSE)
begin_re = re.compile('BEGIN', re.IGNORECASE)
@@ -712,7 +725,8 @@ def parseLine(line, lineNumber = None):
match = line_re.match(line)
if match is None:
raise ParseError("Failed to parse line: %s" % line, lineNumber)
- return (match.group('name'),
+ # Underscores are replaced with dash to work around Lotus Notes
+ return (match.group('name').replace('_','-'),
parseParams(match.group('params')),
match.group('value'), match.group('group'))
@@ -888,7 +902,7 @@ def foldOneLine(outbuf, input, lineLength = 75):
def defaultSerialize(obj, buf, lineLength):
"""Encode and fold obj and its children, write to buf or return a string."""
- outbuf = buf or StringIO.StringIO()
+ outbuf = buf or cStringIO.StringIO()
if isinstance(obj, Component):
if obj.group is None:
@@ -902,26 +916,19 @@ def defaultSerialize(obj, buf, lineLength):
child.serialize(outbuf, lineLength, validate=False)
if obj.useBegin:
foldOneLine(outbuf, str(groupString + u"END:" + obj.name), lineLength)
- if DEBUG: logger.debug("Finished %s" % obj.name.upper())
elif isinstance(obj, ContentLine):
startedEncoded = obj.encoded
if obj.behavior and not startedEncoded: obj.behavior.encode(obj)
- s=StringIO.StringIO() #unfolded buffer
+ s=codecs.getwriter('utf-8')(cStringIO.StringIO()) #unfolded buffer
if obj.group is not None:
- s.write(str(obj.group + '.'))
- if DEBUG: logger.debug("Serializing line" + str(obj))
- s.write(str(obj.name.upper()))
+ s.write(obj.group + '.')
+ s.write(obj.name.upper())
for key, paramvals in obj.params.iteritems():
- s.write(';' + str(key) + '=' + ','.join(map(dquoteEscape, paramvals)).encode("utf-8"))
- if isinstance(obj.value, unicode):
- strout = obj.value.encode("utf-8")
- else:
- strout = obj.value
- s.write(':' + strout)
+ s.write(';' + key + '=' + ','.join(dquoteEscape(p) for p in paramvals))
+ s.write(':' + obj.value)
if obj.behavior and not startedEncoded: obj.behavior.decode(obj)
foldOneLine(outbuf, s.getvalue(), lineLength)
- if DEBUG: logger.debug("Finished %s line" % obj.name.upper())
return buf or outbuf.getvalue()
@@ -957,7 +964,8 @@ class Stack:
def readComponents(streamOrString, validate=False, transform=True,
- findBegin=True, ignoreUnreadable=False):
+ findBegin=True, ignoreUnreadable=False,
+ allowQP=False):
"""Generate one Component at a time from a stream.
>>> import StringIO
@@ -978,7 +986,7 @@ def readComponents(streamOrString, validate=False, transform=True,
stack = Stack()
versionLine = None
n = 0
- for line, n in getLogicalLines(stream, False, findBegin):
+ for line, n in getLogicalLines(stream, allowQP, findBegin):
if ignoreUnreadable:
try:
vline = textLineToContentLine(line, n)
@@ -1031,10 +1039,10 @@ def readComponents(streamOrString, validate=False, transform=True,
def readOne(stream, validate=False, transform=True, findBegin=True,
- ignoreUnreadable=False):
+ ignoreUnreadable=False, allowQP=False):
"""Return the first component from stream."""
return readComponents(stream, validate, transform, findBegin,
- ignoreUnreadable).next()
+ ignoreUnreadable, allowQP).next()
#--------------------------- version registry ----------------------------------
__behaviorRegistry={}
diff --git a/src/vobject/behavior.py b/vobject/behavior.py
index 226c0cc..226c0cc 100644
--- a/src/vobject/behavior.py
+++ b/vobject/behavior.py
diff --git a/src/vobject/hcalendar.py b/vobject/hcalendar.py
index 93614ab..93614ab 100644
--- a/src/vobject/hcalendar.py
+++ b/vobject/hcalendar.py
diff --git a/src/vobject/icalendar.py b/vobject/icalendar.py
index fd18910..09862c1 100644
--- a/src/vobject/icalendar.py
+++ b/vobject/icalendar.py
@@ -4,15 +4,15 @@ import string
import behavior
import dateutil.rrule
import dateutil.tz
-import StringIO
+import StringIO, cStringIO
import datetime
import socket, random #for generating a UID
import itertools
-from base import VObjectError, NativeError, ValidateError, ParseError, \
- VBase, Component, ContentLine, logger, defaultSerialize, \
- registerBehavior, backslashEscape, foldOneLine, \
- newFromBehavior, CRLF, LF
+from base import (VObjectError, NativeError, ValidateError, ParseError,
+ VBase, Component, ContentLine, logger, defaultSerialize,
+ registerBehavior, backslashEscape, foldOneLine,
+ newFromBehavior, CRLF, LF, ascii)
#------------------------------- Constants -------------------------------------
DATENAMES = ("rdate", "exdate")
@@ -30,13 +30,19 @@ twoHours = datetime.timedelta(hours=2)
#---------------------------- TZID registry ------------------------------------
__tzidMap={}
+def toUnicode(s):
+ """Take a string or unicode, turn it into unicode, decoding as utf-8"""
+ if isinstance(s, str):
+ s = s.decode('utf-8')
+ return s
+
def registerTzid(tzid, tzinfo):
"""Register a tzid -> tzinfo mapping."""
- __tzidMap[tzid]=tzinfo
+ __tzidMap[toUnicode(tzid)]=tzinfo
def getTzid(tzid):
"""Return the tzid if it exists, or None."""
- return __tzidMap.get(tzid, None)
+ return __tzidMap.get(toUnicode(tzid), None)
utc = dateutil.tz.tzutc()
registerTzid("UTC", utc)
@@ -83,7 +89,8 @@ class TimezoneComponent(Component):
# workaround for dateutil failing to parse some experimental properties
good_lines = ('rdate', 'rrule', 'dtstart', 'tzname', 'tzoffsetfrom',
'tzoffsetto', 'tzid')
- buffer = StringIO.StringIO()
+ # serialize encodes as utf-8, cStringIO will leave utf-8 alone
+ buffer = cStringIO.StringIO()
# allow empty VTIMEZONEs
if len(self.contents) == 0:
return None
@@ -97,7 +104,8 @@ class TimezoneComponent(Component):
customSerialize(comp)
foldOneLine(buffer, u"END:" + obj.name)
customSerialize(self)
- return dateutil.tz.tzical(StringIO.StringIO(str(buffer.getvalue()))).get()
+ buffer.seek(0) # tzical wants to read a stream
+ return dateutil.tz.tzical(buffer).get()
def settzinfo(self, tzinfo, start=2000, end=2030):
"""Create appropriate objects in self to represent tzinfo.
@@ -269,22 +277,22 @@ class TimezoneComponent(Component):
return None
# try PyICU's tzid key
if hasattr(tzinfo, 'tzid'):
- return tzinfo.tzid
+ return toUnicode(tzinfo.tzid)
# try pytz zone key
if hasattr(tzinfo, 'zone'):
- return tzinfo.zone
+ return toUnicode(tzinfo.zone)
# try tzical's tzid key
elif hasattr(tzinfo, '_tzid'):
- return tzinfo._tzid
+ return toUnicode(tzinfo._tzid)
else:
# return tzname for standard (non-DST) time
notDST = datetime.timedelta(0)
for month in xrange(1,13):
dt = datetime.datetime(2000, month, 1)
if tzinfo.dst(dt) == notDST:
- return tzinfo.tzname(dt)
+ return toUnicode(tzinfo.tzname(dt))
# there was no standard time in 2000!
raise VObjectError("Unable to guess TZID for tzinfo %s" % str(tzinfo))
@@ -646,7 +654,7 @@ class DateTimeBehavior(behavior.Behavior):
obj.params['X-VOBJ-FLOATINGTIME-ALLOWED'] = ['TRUE']
if obj.params.get('TZID'):
# Keep a copy of the original TZID around
- obj.params['X-VOBJ-ORIGINAL-TZID'] = obj.params['TZID']
+ obj.params['X-VOBJ-ORIGINAL-TZID'] = [obj.params['TZID']]
del obj.params['TZID']
return obj
@@ -661,7 +669,7 @@ class DateTimeBehavior(behavior.Behavior):
obj.tzid_param = tzid
if obj.params.get('X-VOBJ-ORIGINAL-TZID'):
if not hasattr(obj, 'tzid_param'):
- obj.tzid_param = obj.params['X-VOBJ-ORIGINAL-TZID']
+ obj.tzid_param = obj.x_vobj_original_tzid_param
del obj.params['X-VOBJ-ORIGINAL-TZID']
return obj
@@ -685,7 +693,7 @@ class DateOrDateTimeBehavior(behavior.Behavior):
if getattr(obj, 'value_param', 'DATE-TIME').upper() == 'DATE-TIME':
if hasattr(obj, 'tzid_param'):
# Keep a copy of the original TZID around
- obj.params['X-VOBJ-ORIGINAL-TZID'] = obj.tzid_param
+ obj.params['X-VOBJ-ORIGINAL-TZID'] = [obj.tzid_param]
del obj.tzid_param
return obj
@@ -838,9 +846,10 @@ class VCalendar2_0(VCalendarComponentBehavior):
findTzids(child, table)
findTzids(obj, tzidsUsed)
- oldtzids = [x.tzid.value for x in getattr(obj, 'vtimezone_list', [])]
+ oldtzids = [toUnicode(x.tzid.value) for x in getattr(obj, 'vtimezone_list', [])]
for tzid in tzidsUsed.keys():
- if tzid != 'UTC' and tzid not in oldtzids:
+ tzid = toUnicode(tzid)
+ if tzid != u'UTC' and tzid not in oldtzids:
obj.add(TimezoneComponent(tzinfo=getTzid(tzid)))
registerBehavior(VCalendar2_0)
diff --git a/src/vobject/ics_diff.py b/vobject/ics_diff.py
index 4aaaef9..4aaaef9 100644
--- a/src/vobject/ics_diff.py
+++ b/vobject/ics_diff.py
diff --git a/src/vobject/vcard.py b/vobject/vcard.py
index 01d1d42..01d1d42 100644
--- a/src/vobject/vcard.py
+++ b/vobject/vcard.py
diff --git a/src/vobject/win32tz.py b/vobject/win32tz.py
index 35f997b..35f997b 100644
--- a/src/vobject/win32tz.py
+++ b/vobject/win32tz.py