Issue #44 resolved

"protocol u'NSSpeechSynthesizerDelegate' does not exist"

vkhromov avatarvkhromov created an issue
$ sw_vers -productVersion 
10.8.2

$ python
Python 2.7.2 (v2.7.2:8527427914a2, Jun 11 2011, 15:22:34) 
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import objc
>>> from AppKit import *
>>> objc.protocolNamed("NSLocking")
<objc.formal_protocol NSLocking at 0x105a9fd80>
>>> objc.protocolNamed("NSSpeechSynthesizerDelegate")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/qwerty/.python/lib/python/pyobjc_core-2.4-py2.7-macosx-10.8-intel.egg/objc/_protocols.py", line 33, in protocolNamed
    raise ProtocolError("protocol %r does not exist" % (name,), name)
objc.ProtocolError: ("protocol u'NSSpeechSynthesizerDelegate' does not exist", u'NSSpeechSynthesizerDelegate')
>>> 

Comments (10)

  1. Ronald Oussoren

    I've attached a rough patch that will solve this particular problem.

    I haven't applied the patch yet because I don't particularly like it. I first want to check if it is possible to force the runtime to load all protocol definitions, even if they don't appear to be used.

    If that doesn't work this patch will be used, although properly in a more generic manner and in a way that won't cause compile errors on older OSX releases.

  2. vkhromov

    Yes, it's much better to fix the problem properly because NSSpeechSynthesizerDelegate isn't the only protocol PyObjC doesn't know:

    $ cat ./not-existing-protocol.py
    #! /usr/bin/python
    
    import objc
    import sys
    from AppKit import *
    
    for l in sys.stdin:
        l = l.rstrip()
        try:
            objc.protocolNamed(l)
            c = ' '
        except:
            c = '-'
        print "{0} {1}".format(c, l)
    
    $ sed -n 's|@protocol.*\(NS.*\);|\1|p' /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.8.sdk/System/Library/Frameworks/AppKit.framework/Versions/C/Headers/*.h | sort -u | ./not-existing-protocol.py 
    - NSAlertDelegate
      NSAnimationDelegate
    - NSApplicationDelegate
      NSBrowserDelegate
    - NSCollectionViewDelegate
      NSComboBoxCellDataSource
      NSComboBoxDataSource
    - NSDatePickerCellDelegate
      NSDraggingSource
    - NSDrawerDelegate
    - NSImageDelegate
      NSLayoutManagerDelegate
      NSMatrixDelegate
      NSMenuDelegate
      NSMenuItem
      NSOpenSavePanelDelegate
      NSOutlineViewDataSource
    - NSPageControllerDelegate
      NSPasteboardItemDataProvider
      NSPasteboardWriting
      NSPathControlDelegate
      NSPopoverDelegate
      NSRuleEditorDelegate
    - NSSharingServiceDelegate
    - NSSharingServicePickerDelegate
    - NSSoundDelegate
    - NSSpeechRecognizerDelegate
    - NSSpeechSynthesizerDelegate
    - NSSplitViewDelegate
    - NSTabViewDelegate
      NSTableViewDataSource
      NSTextDelegate
      NSTextFieldDelegate
      NSTextFinderBarContainer
    - NSTextStorageDelegate
      NSTextViewDelegate
    - NSTokenFieldCellDelegate
    - NSTokenFieldDelegate
      NSToolbarDelegate
      NSWindowDelegate
    
  3. vkhromov

    Also it should be very helpfull to have something like "register_formal_protocol" function uses objc_getProtocol, and to be able to call it directly from Python code via PyObjC. Then registering unknown to PyObjC protocols will not require recompiling PyObjC code.

  4. Ronald Oussoren

    Doing it "properly" might end up being the hack I posted earlier because the ObjC runtime doesn't seem to instantiate protocols unless they are actually used (at least on OSX 10.8, I haven't checked other OS X releases yet).

    That is, even the Cocoa functions for getting a protocol fail: Cocoa.NSProtocolFromString('NSCollectionViewDelegate')

    I have located the ObjC runtime source code, but haven't tried yet to find the code that realizes protocols. With some luck it will be possible to force the runtime to realize all protocols, not just the onces referenced from code. If that is possible there won't have to be an API to create a formal_protocol for a not-yet-loaded protocol.

    Anyway, just calling objc_getProtocol isn't good enough, I've tried this and that fails to find NSSpeechSynthesizerDelegate unless the protocol is already realized due to a reference in code (an (at)protocol line or a class that implements the protocol).

    This situation sucks. I'll probably end up filing a bugreport at Apple, but that won't help until OSX 10.9 at the earliest (if this ever gets fixed).

    UPDATE: Changed text to replace an at-sign by (at) to avoid a link to a person page.

  5. Ronald Oussoren

    Interesting...

    It seems that protocols that aren't used internally aren't actually present in the shared library image.

    That is, if you dump the contents of the objc section with otool ("otool -vo /System/Library/Frameworks/AppKit.framework/AppKit") you can find "NSToolbarDelegate" (one of the protocols you could fetch with objc.protocolNamed), in the output but not "NSTextStorageDelegate" (a protocol that cannot be fetched with objc.protocolNamed).

    This indicates that the compiler won't generate code for a protocol unless the protocol is referenced somewhere.

    That would mean that I'll have go generate code that references all protocols defined in a framework, just to be sure. Generating that code isn't hard, but I had hoped to remove more C code, not add new code.

  6. Ronald Oussoren

    Add (or enhance the existing) C extension that uses all protocols

    This is needed because framework only export those protocol definitions that are used by the code in that framework. Without the extensions some protocols are not available to python code, see issue #44 for more information.

    Note: the various *_protocols.m files are generated.

    Fixes issue #44

    → <<cset 95993d2b24b3>>

  7. Ronald Oussoren

    Add (or enhance the existing) C extension that uses all protocols

    This is needed because framework only export those protocol definitions that are used by the code in that framework. Without the extensions some protocols are not available to python code, see issue #44 for more information.

    Note: the various *_protocols.m files are generated.

    Fixes issue #44

    → <<cset 95993d2b24b3>>

  8. Ronald Oussoren

    I ended up with adding an extension to a number of framework wrappers to ensure that all protocols are available to Python code.

    The issue should now be fixed in the tip of the default and pyobjc-2.5.x branches.

    Thanks for reporting this!

  9. vkhromov

    Thank you for fixing it! I'm going to test it soon.

    BTW, it looks like that the original fix doesn't required anymore and can be removed from sources.

  10. Log in to comment
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.