PyDocURLProtocol

This example implements a subclass of NSURLProtocol that can be used to load the pydoc documentation of a module.

It also includes a simple documentation browser using WebKit and the PyDocURLProtocol class.

Sources

PyDocBrowser.py

from PyObjCTools import AppHelper

import Foundation, AppKit, WebKit
from Foundation import *
from AppKit import *
import objc; objc.setVerbose(1)

import PyDocURLProtocol
import PyDocEvents

PyDocURLProtocol.setup()

# the web browser doesn't have or need any code really

if __name__ == '__main__':
    AppHelper.runEventLoop()

PyDocEvents.py

"""
Minimal applescript support.

The PyDocEventHandler handles just the event that is used to open URLs. Thanks
to this class you can use ``open pydoc:///os.open`` from a command-line, or
add ``pydoc:///`` to HTML files.
"""
from Foundation import *

from Carbon.AppleEvents import kAEISGetURL, kAEInternetSuite
import struct
import objc

def fourCharToInt(code):
    return struct.unpack('>l', code)[0]

class PyDocEventHandler (NSObject):
    webview = objc.IBOutlet('webview')
    urlfield = objc.IBOutlet('urlfield')

    def handleEvent_withReplyEvent_(self, event, replyEvent):
        theURL = event.descriptorForKeyword_(fourCharToInt('----'))

        self.urlfield.setStringValue_(theURL.stringValue())
        self.webview.takeStringURLFrom_(theURL)


    def awakeFromNib(self):
        manager = NSAppleEventManager.sharedAppleEventManager()

        # Add a handler for the event GURL/GURL. One might think that
        # Carbon.AppleEvents.kEISInternetSuite/kAEISGetURL would work,
        # but the system headers (and hence the Python wrapper for those)
        # are wrong.
        manager.setEventHandler_andSelector_forEventClass_andEventID_(
            self, 'handleEvent:withReplyEvent:',
                fourCharToInt('GURL'),
                fourCharToInt('GURL'))

PyDocURLProtocol.py

from Foundation import *
import objc
from pydochelper import gethtmldoc

PYDOCSCHEME = u'pydoc'

class PyDocURLProtocol(NSURLProtocol):

    def canInitWithRequest_(klass, request):
        if request.URL().scheme() == PYDOCSCHEME:
            return True
        return False

    def canonicalRequestForRequest_(klass, request):
        return request

    def startLoading(self):
        client = self.client()
        request = self.request()
        urlpath = request.URL().standardizedURL().path()
        modpath = urlpath.replace(u'/', u'.'
            ).lstrip(u'.'
            ).replace(u'.html', u'')

        try:
            data = gethtmldoc(modpath.encode('utf-8'))
        except Exception, e:
            client.URLProtocol_didFailWithError_(
                self,
                NSError.errorWithDomain_code_userInfo_(
                    NSURLErrorDomain,
                    NSURLErrorResourceUnavailable,
                    None,
                ),
            )
        else:
            response = NSURLResponse.alloc().initWithURL_MIMEType_expectedContentLength_textEncodingName_(
                request.URL(),
                u'text/html',
                len(data),
                u'utf-8',
            )
            client.URLProtocol_didReceiveResponse_cacheStoragePolicy_(
                self,
                response,
                NSURLCacheStorageNotAllowed,
            )
            client.URLProtocol_didLoadData_(
                self,
                buffer(data),
            )
            client.URLProtocolDidFinishLoading_(self)

    def stopLoading(self):
        pass

def setup():
    NSURLProtocol.registerClass_(PyDocURLProtocol)

def teardown():
    NSURLProtocol.unregisterClass_(PyDocURLProtocol)

def main(*args):
    if not args:
        args = ('dict',)
    setup()
    for arg in args:
        url = NSURL.URLWithString_(u'pydoc:///%s' % (arg,))
        print NSString.stringWithContentsOfURL_(url)
    teardown()

import sys
if __name__ == '__main__': main(*sys.argv[1:])

pydochelper.py

import pydoc

__all__ = ['gethtmldoc']

def gethtmldoc(thing, forceload=0):
    obj, name = pydoc.resolve(thing, forceload)
    page = pydoc.html.page(
        pydoc.describe(obj),
        pydoc.html.document(obj, name)
    )
    return page

setup.py

"""
Script for building the example.

Usage:
    python setup.py py2app
"""
from distutils.core import setup
import py2app

plist = dict(
            NSMainNibFile='PyDocBrowser',
            NSAppleScriptEnabled=True,
            CFBundleURLTypes=[
                dict(
                    CFBundleURLName='Python Documention URL',
                    CFBundleURLSchemes=['pydoc'],
                )
            ]
        )
setup(
    app=["PyDocBrowser.py"],
    data_files=["PyDocBrowser.nib"],
    options=dict(py2app=dict(plist=plist)),
)

Resources

Table Of Contents

Resources

Support development