[Vobject] copy.deepcopy on a calendar

Cyrus Daboo cyrus+lists.vobject at daboo.name
Wed Feb 22 13:53:34 CST 2006


Hi,
I am trying to duplicate a calendar using copy.deepcopy, but have run into 
a problem. Basically, whenever a DATE-TIME value in the iCal component 
contains a named timezone, the deepcopy fails with:

copy.Error: un(deep)copyable object of type <type 'thread.lock'>

Setting the date-time value to UTC or floating results in deepcopy working. 
Also, doing a 'transformFromNative' on the calendar Component and all its 
children also fixes the problem. i.e. the problem seems to be related to 
use of an explicit timezone object in a datetime.datetime value in a 
property.

Attached is some code that illustrates the problem.

I believe the issue is caused by the self._cache_lock in class rrulebase in 
rrule.py in dateutil. However, I'm not sure if it can be resolved there.

Some possible workarounds:

1) Do 'transformFromNative', then do deepcopy, then transform back each 
time a duplicate component is needed.

2) Fix dateutil.

3) Add a 'duplicate' method in vobject that does not use deepcopy.

I ended up doing the later and that works well. However I'm not sure 
whether others would want this. Attached is a patch to base.py that does 
this - please comment.

-- 
Cyrus Daboo
-------------- next part --------------
import base, behavior, icalendar, vcard
import copy

icaltest1=r"""BEGIN:VCALENDAR
CALSCALE:GREGORIAN
X-WR-TIMEZONE;VALUE=TEXT:US/Pacific
METHOD:PUBLISH
PRODID:-//Apple Computer\, Inc//iCal 1.0//EN
X-WR-CALNAME;VALUE=TEXT:Example
VERSION:2.0
BEGIN:VEVENT
SEQUENCE:5
DTSTART;TZID=US/Pacific:20021028T140000
DTSTAMP:20021028T011706Z
SUMMARY:Coffee with Jason
UID:EC9439B1-FF65-11D6-9973-003065F99D04
DTEND;TZID=US/Pacific:20021028T150000
END:VEVENT
BEGIN:VTIMEZONE
X-LIC-LOCATION:Random location
TZID:US/Pacific
LAST-MODIFIED:19870101T000000Z
BEGIN:STANDARD
DTSTART:19671029T020000
RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
TZOFFSETFROM:-0700
TZOFFSETTO:-0800
TZNAME:PST
END:STANDARD
BEGIN:DAYLIGHT
DTSTART:19870405T020000
RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4
TZOFFSETFROM:-0800
TZOFFSETTO:-0700
TZNAME:PDT
END:DAYLIGHT
END:VTIMEZONE
END:VCALENDAR"""

icaltest2=r"""BEGIN:VCALENDAR
CALSCALE:GREGORIAN
X-WR-TIMEZONE;VALUE=TEXT:US/Pacific
METHOD:PUBLISH
PRODID:-//Apple Computer\, Inc//iCal 1.0//EN
X-WR-CALNAME;VALUE=TEXT:Example
VERSION:2.0
BEGIN:VEVENT
SEQUENCE:5
DTSTART:20021028T140000Z
DTSTAMP:20021028T011706Z
SUMMARY:Coffee with Jason
UID:EC9439B1-FF65-11D6-9973-003065F99D04
DTEND:20021028T150000Z
END:VEVENT
END:VCALENDAR"""


def doCopy(name, calname):
    try:
        cal = base.readOne(calname)
        copy.deepcopy(cal)
    except copy.Error, txt:
        print "deepcopy failed for file %s with error %s" % (name, txt)
    else:
        print "deepcopy worked for file %s" % (name,)

if __name__ == '__main__':
    doCopy("icaltest1", icaltest1)
    doCopy("icaltest2", icaltest2)
    
-------------- next part --------------
Index: src/vobject/base.py
===================================================================
--- src/vobject/base.py	(revision 131)
+++ src/vobject/base.py	(working copy)
@@ -1,5 +1,6 @@
 """vobject module for reading vCard and vCalendar files."""
 
+import copy
 import re
 import sys
 import logging
@@ -56,6 +57,12 @@
         self.parentBehavior = None
         self.isNative = False
     
+    def copy(self, copyit):
+        self.group = copyit.group
+        self.behavior = copyit.behavior
+        self.parentBehavior = copyit.parentBehavior
+        self.isNative = copyit.isNative
+        
     def validate(self, *args, **kwds):
         """Call the behavior's validate method, or return True."""
         if self.behavior:
@@ -245,6 +252,21 @@
         if qp:
             self.value = str(self.value).decode('quoted-printable')
 
+    @classmethod
+    def duplicate(clz, copyit):
+        newcopy = clz('', {}, '')
+        newcopy.copy(copyit)
+        return newcopy
+
+    def copy(self, copyit):
+        super(ContentLine, self).copy(copyit)
+        self.name = copyit.name
+        self.value = copy.copy(copyit.value)
+        self.encoded = self.encoded
+        self.params = copy.copy(copyit.params)
+        self.singletonparams = copy.copy(copyit.singletonparams)
+        self.lineNumber = copyit.lineNumber
+        
     def __eq__(self, other):
         try:
             return (self.name == other.name) and (self.params == other.params) and (self.value == other.value)
@@ -348,6 +370,27 @@
         
         self.autoBehavior()
 
+    @classmethod
+    def duplicate(clz, copyit):
+        newcopy = clz()
+        newcopy.copy(copyit)
+        return newcopy
+
+    def copy(self, copyit):
+        super(Component, self).copy(copyit)
+        
+        # deep copy of contents
+        self.contents = {}
+        for key, lvalue in copyit.contents.items():
+            newvalue = []
+            for value in lvalue:
+                newitem = value.duplicate(value)
+                newvalue.append(newitem)
+            self.contents[key] = newvalue
+
+        self.name = copyit.name
+        self.useBegin = copyit.useBegin
+         
     def setProfile(self, name):
         """Assign a PROFILE to this unnamed component.
         


More information about the VObject mailing list