PyDocURLProtocol¶
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"],
)