# PyDocURLProtocol

- `Download example`

A PyObjC Example without documentation

## Sources

### PyDocBrowser.py

    import AppKit  # noqa: F401
    import Foundation  # noqa: F401
    import PyDocEvents  # noqa: F401
    import PyDocURLProtocol  # noqa: F401
    import WebKit  # noqa: F401
    from PyObjCTools import AppHelper

    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.
    """
    import struct

    import objc
    from Cocoa import NSAppleEventManager, NSObject

    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(b"----"))

            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(b"GURL"),
                fourCharToInt(b"GURL"),
            )

### PyDocURLProtocol.py

    import sys

    from Cocoa import (
        NSURL,
        NSData,
        NSError,
        NSString,
        NSURLCacheStorageNotAllowed,
        NSURLErrorDomain,
        NSURLErrorResourceUnavailable,
        NSURLProtocol,
        NSURLResponse,
    )
    from pydochelper import gethtmldoc

    PY3K = sys.version_info[0] == 3

    PYDOCSCHEME = "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("/", ".").lstrip(".").replace(".html", "")

            if not PY3K:
                modpath = modpath.encode("utf-8")

            try:
                data = gethtmldoc(modpath)
                if PY3K:
                    data = data.encode("utf-8")
            except Exception:
                client.URLProtocol_didFailWithError_(
                    self,
                    NSError.errorWithDomain_code_userInfo_(
                        NSURLErrorDomain, NSURLErrorResourceUnavailable, None
                    ),
                )
            else:
                response = NSURLResponse.alloc().initWithURL_MIMEType_expectedContentLength_textEncodingName_(  # noqa: B950
                    request.URL(), "text/html", len(data), "utf-8"
                )
                client.URLProtocol_didReceiveResponse_cacheStoragePolicy_(
                    self, response, NSURLCacheStorageNotAllowed
                )
                client.URLProtocol_didLoadData_(
                    self, NSData.dataWithBytes_length_(data, len(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()
        try:
            for arg in args:
                url = NSURL.URLWithString_(f"pydoc:///{arg}")
                print(NSString.stringWithContentsOfURL_(url))
        finally:
            teardown()

    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:
        python3 setup.py py2app
    """
    from setuptools import setup

    plist = {
        "NSMainNibFile": "PyDocBrowser",
        "NSAppleScriptEnabled": True,
        "CFBundleURLTypes": [
            {"CFBundleURLName": "Python Documentation URL", "CFBundleURLSchemes": ["pydoc"]}
        ],
    }

    setup(
        app=["PyDocBrowser.py"],
        data_files=["PyDocBrowser.nib"],
        options={"py2app": {"plist": plist}},
        setup_requires=["py2app", "pyobjc-framework-Cocoa", "pyobjc-framework-WebKit"],
    )