Re: [Pyobjc-dev] Possible bug in PyObjC bridge (SyncService)
Brought to you by:
ronaldoussoren
From: Ronald O. <ron...@ma...> - 2010-05-04 16:05:20
|
On 4 May, 2010, at 17:44, Mani Ghasemlou wrote: > On Fri, Apr 30, 2010 at 1:40 AM, Ronald Oussoren <ron...@ma...> wrote: >> >> On 29 Apr, 2010, at 17:57, Mani Ghasemlou wrote: >> >>> On Thu, Apr 29, 2010 at 11:45 AM, Aahz <aa...@py...> wrote: >>>> On Thu, Apr 29, 2010, Mani Ghasemlou wrote: >>>>> >>>>> Users of my app have been complaining of the following error: >>>>> >>>>> ValueError: NSInvalidArgumentException - *** -encodeInt:forKey: only >>>>> defined for abstract class. Define -[NSArchiver encodeInt:forKey:]! >>>>> >>>>> It occurs in the following line of code: >>>>> >>>>> session.prepareToPullChangesForEntityNames_beforeDate_(entityNames, >>>>> NSDate.distantFuture()) >>>>> >>>>> Where "session" is an ISyncSession object >>>>> (http://developer.apple.com/mac/library/documentation/cocoa/Reference/SyncServicesFramework/Classes/ISyncSession_Class/Reference/Reference.html#//apple_ref/occ/cl/ISyncSession), >>>>> and "entityNames" is a Python list of strings. >>>> >>>> What versions of Python, PyObjC, and OSX are you using? What's the full >>>> traceback? What is the value of entityNames? How do you create session? >>> >>> Hi Aahz! >>> >>> I'm building my app on a 10.5.8 system, with standard versions of >>> Python and PyObjC (2.5, and 2.0 respectively). I have not determined >>> if the errors occur on specific versions of OSX, but we QA the >>> application on both Leopard and Snow Leopard and internally we cannot >>> reproduce the problem (yet). >>> >>> The error posted is the full traceback. There is nothing else. >>> >>> The value of entityNames is *always* the following: >>> >>> entityNames = ['com.apple.contacts.Contact', >>> 'com.apple.contacts.Email Address', >>> 'com.apple.calendars.Calendar', >>> 'com.apple.calendars.Event', >>> 'com.apple.calendars.Recurrence', >>> 'com.apple.calendars.Attendee'] >>> >>> The session is created using >>> "ISyncSession.beginSessionWithClient_entityNames_beforeDate_". The >>> prepareToPullChanges... code would not be invoked if creating the >>> session returns a nil/None session object, or if it raises any >>> exception. >> >> I haven't used SyncServices for anything serious yet, which means I could be completely wrong with my assumptions. >> >> IIUC prepareToPullChanges... ends the phase where you push updates to SyncServices and starts the phase where SyncServices pushes changes to you. What kind of objects do you push to SyncServices? Are those pure Objective-C, instances of PyObjC subclasses or pure Python? >> >> The exception could occur when you push Python objects to SyncServices and SyncServices tries to serialize them using NSArchiver that doesn't support keyed coding, the code in Lib/objc/_pycoder.py assumes all archivers support keyed coding. >> >> Ronald > > Hello Ronald, > > The data pushed is a Python list of Python dicts. The dicts' keys are > always strings, and the values are a mix of: > > - Python strings > - Python ints > - Python lists of Python strings & lists of Python ints > - None objects > - NSDatetime objects, created using > NSCalendarDate.dateWithTimeIntervalSince1970_(python_int) > - Python lists of NSDatetime objects (created same way as above) > > Your suggestion that the error could be caused by one or more of the > records I am pushing helps me. I will try running tests with Calendar > events (the main type of data I am syncing) that make use of all the > above types and see if any of them trigger the error. > > I will be posting again with my findings sometime this week, but > please let me know if there is something specific from the above that > I should test for. You could try a sync action with 'objc.setVerbose(1)', that will print exception traces when translating exceptions to ObjC and tends to be helpful when an exception is raised in Python code and then transitions through an ObjC layer. If this traceback contains references to objc._pycoder my hypothesis is correct, and that means that that module should be enhanced to support both keyed coding and classic non-keyed coding. BTW. All values you mention except 'None' should encode just fine using a "classic" coder, replacing 'None' by NSNull.null() before encoding might be the quickest solution to your problem. It looks like this is relatively easy to enhance the unittests of _pycoder to test with classic coders, my first result is a collection of failed tests: ====================================================================== ERROR: test_structseq (__main__.TestArchivePlainPython) ---------------------------------------------------------------------- Traceback (most recent call last): File "/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/test/pickletester.py", line 583, in test_structseq s = self.dumps(t, proto) File "test_archive_python.py", line 436, in dumps return NSArchiver.archivedDataWithRootObject_(arg) ValueError: NSInvalidArgumentException - *** -encodeInt:forKey: only defined for abstract class. Define -[NSArchiver encodeInt:forKey:]! Fixing the tests is more work, but should be relatively straightforward. Ronald > > Thanks! > Mani |