Commits

Anonymous committed e46d4e1

Adding a package manager implementation I started last night so Bob can work on it.

  • Participants
  • Parent commits ef4ade7

Comments (0)

Files changed (6)

File pyobjc/Examples/Twisted/PackMan/MainMenu.nib/classes.nib

+{
+    IBClasses = (
+        {
+            CLASS = DatabasesController; 
+            LANGUAGE = ObjC; 
+            OUTLETS = {databaseTable = id; }; 
+            SUPERCLASS = NSObject; 
+        }, 
+        {CLASS = FirstResponder; LANGUAGE = ObjC; SUPERCLASS = NSObject; }, 
+        {
+            ACTIONS = {filter = id; install = id; }; 
+            CLASS = PackageController; 
+            LANGUAGE = ObjC; 
+            OUTLETS = {databaseList = id; descriptionField = id; table = id; }; 
+            SUPERCLASS = NSObject; 
+        }
+    ); 
+    IBVersion = 1; 
+}

File pyobjc/Examples/Twisted/PackMan/MainMenu.nib/info.nib

+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>IBDocumentLocation</key>
+	<string>851 8 516 438 0 0 1440 878 </string>
+	<key>IBEditorPositions</key>
+	<dict>
+		<key>29</key>
+		<string>132 280 318 44 0 0 1440 878 </string>
+	</dict>
+	<key>IBFramework Version</key>
+	<string>349.0</string>
+	<key>IBOpenObjects</key>
+	<array>
+		<integer>29</integer>
+		<integer>21</integer>
+	</array>
+	<key>IBSystem Version</key>
+	<string>7B85</string>
+</dict>
+</plist>

File pyobjc/Examples/Twisted/PackMan/MainMenu.nib/keyedobjects.nib

Binary file added.

File pyobjc/Examples/Twisted/PackMan/PackMan.py

+
+from StringIO import StringIO
+
+## Install corefoundation reactor
+import twisted.internet.cfreactor
+reactor = twisted.internet.cfreactor.install()
+
+## New HTTP client in Twisted/sandbox/moshez
+import newclient
+
+## ObjC stuff
+from Foundation import *
+from PyObjCTools import NibClassBuilder, AppHelper
+from objc import YES, NO, selector
+
+## plistlib from macpython
+import plistlib
+
+
+NibClassBuilder.extractClasses("MainMenu")
+
+
+class DatabasesController(NSObject):
+    def numberOfRowsInTableView_(self, view):
+        return 1
+
+    def tableView_objectValueForTableColumn_row_(self, view, column, row):
+        return "Standard"
+
+    def tableViewSelectionDidChange_(self, aNotification):
+        pass
+
+
+class PackageController(NibClassBuilder.AutoBaseClass):
+    packages = []
+    _allPackages = []
+
+    def applicationDidFinishLaunching_(self, aNotification):
+        self.databaseList.selectRowIndexes_byExtendingSelection_(
+            NSIndexSet.indexSetWithIndex_(0), NO
+        )
+        o = newclient.opener()
+        d = o.open(
+            newclient.Request('http://undefined.org/python/pimp/darwin-6.6-Power_Macintosh.plist')
+        ).addCallback(
+            newclient.read
+        ).addCallback(
+            self.gotPlist
+        ).addErrback(
+            self.errorOpening
+        )
+        reactor.run()
+
+    def gotPlist(self, pliststring):
+        fl = StringIO(pliststring)
+        plist = plistlib.Plist.fromFile(fl)
+        packages = plist['Packages']
+        packages.sort(lambda x, y: cmp(x['Name'].lower(), y['Name'].lower()))
+        self._allPackagesIncludingHidden = packages
+        self._allPackages = self.packages = [x for x in packages if x.get('Version', None)]
+        self.table.reloadData()
+
+    def errorOpening(self, failure):
+        print "error opening url", failure
+
+    def install_(self, sender):
+        print "SELECTED", self.packages[self.table.selectedRow()]['Name']
+
+    def numberOfRowsInTableView_(self, view):
+        return len(self.packages)
+
+    def tableView_objectValueForTableColumn_row_(self, view, column, row):
+        columnName = column.headerCell().stringValue()
+        return getattr(self, 'column_%s' % columnName, lambda row: '')(row)
+
+    def column_Installed(self, row):
+        return "No"
+
+    def column_Package(self, row):
+        return self.packages[row]['Name']
+
+    def column_Version(self, row):
+        return self.packages[row].get('Version', 'No Version')
+
+    def tableViewSelectionDidChange_(self, aNotification):
+        row = self.table.selectedRow()
+        self.descriptionField.setStringValue_(
+            self.packages[row]['Description'].strip()
+        )
+
+    def filter_(self, sender):
+        search = sender.stringValue()
+        self.packages = [
+            package for package in self._allPackages if search.lower() in package['Name'].lower()
+        ]
+        self.table.reloadData()
+
+
+if __name__ == "__main__":
+    AppHelper.runEventLoop()

File pyobjc/Examples/Twisted/PackMan/buildapp.py

+from bundlebuilder import buildapp
+
+buildapp(
+    mainprogram = "PackMan.py",
+    resources = ["MainMenu.nib"],
+    includeModules=['newclient'],
+    symlink = True,
+    nibname = "MainMenu"
+)

File pyobjc/Examples/Twisted/PackMan/newclient.py

+"""
+Example usage:
+o = opener()
+o.open("http://www.yahoo.com/").addCallback(read).addCallback(util.println)
+o.open("http://www.yahoo.com/").addCallback(download(sys.stdout.write))
+d = o.open("http://www.yahoo.com/")
+d.addCallback(download(file("yahoo.html", 'wb'))
+d.addCallback(close)
+"""
+from twisted.internet import protocol, defer
+from twisted.protocols import http
+import urllib2, urlparse
+
+class Request(urllib2.Request):
+
+    def __init__(self, url, data=None, headers={}, visited=None):
+        urllib2.Request.__init__(self, url, data, headers)
+        if visited is None:
+            visited = {}
+        self.visited = visited
+
+
+class Response:
+
+    def __init__(self, version, status, message):
+        self.version = version
+        self.status = status
+        self.message = message
+        self.headers = {}
+        self._events = []
+
+    def handleHeader(self, key, value):
+        self.headers.setdefault(key.lower(), []).append(value)
+
+    def setHandler(self, dataReceived, connectionLost, dataDone):
+        self.dataReceived = dataReceived
+        self.connectionLost = connectionLost
+        self.dataDone = dataDone
+        for event in self._events:
+            getattr(self, event[0])(*event[1:])
+        del self._events
+
+    def dataReceived(self, data):
+        self._events.append(('dataReceived', data))
+    def connectionLost(self, reason):
+        self._events.append(('connectionLost', reason))
+    def dataDone(self):
+        self._events.append(('dataDone',))
+
+class HTTPClient(http.HTTPClient):
+
+    def connectionMade(self):
+        self.factory.connection.callback(self)
+
+    def setResponseSink(self, deferred):
+        self.sink = deferred
+
+    def handleStatus(self, version, status, message):
+        self.response = Response(version, status, message)
+        self.handleHeader = self.response.handleHeader
+
+    def handleEndHeaders(self):
+        self.sink.callback(self.response)
+        def _(reason):
+            http.HTTPClient.connectionLost(self, reason)
+            if not self.done:
+                self.response.connectionLost(reason)
+        self.connectionLost = _
+
+    def connectionLost(self, reason):
+        http.HTTPClient.connectionLost(self, reason)
+        self.sink.errback(reason)
+
+    def handleResponsePart(self, data):
+        self.response.dataReceived(data)
+
+    def handleResponseEnd(self):
+        self.done = 1
+        self.response.dataDone()
+
+class ClientFactory(protocol.ClientFactory):
+    protocol = HTTPClient
+    def __init__(self):
+        self.connection = defer.Deferred()
+    def connectionFailed(self, _, reason):
+        self.connection.errback(reason)
+
+class NoHandler(LookupError):
+    pass
+
+def sendHeaders(connection, req):
+    if req.has_data():
+        data = req.get_data()
+        connection.sendCommand('POST', requ.get_selector())
+        if not req.headers.has_key('Content-type'):
+            connection.sendHeader('Content-type',
+                                  'application/x-www-form-urlencoded')
+        if not req.headers.has_key('Content-length'):
+            connection.sendHeader('Content-length', str(len(data)))
+    else:
+        connection.sendCommand('GET', req.get_selector())
+    connection.sendHeader('Host', req.get_host())
+    for el in req.headers.iteritems():
+        connection.sendHeader(*el)
+    connection.endHeaders()
+    if req.has_data():
+        connection.transport.write(data)
+    return connection
+
+class Opener:
+
+    def __init__(self, *handlers):
+        self.handlers = list(handlers)
+        for handler in self.handlers:
+            handler.setOpener(self)
+
+    def callMethod(self, name, *args, **kw):
+        value = None
+        for handler in self.handlers:
+            value = getattr(handler, name, lambda *args,**kw:None)(*args, **kw)
+            if value is not None:
+                break
+        if value is None:
+            raise NoHandler("No handlers found for method %s" % name)
+        return value
+
+    def open(self, request, factory=None):
+        try:
+            self.callMethod('transform', request)
+        except NoHandler:
+            pass
+        factory = ClientFactory()
+        self.callMethod('connect_'+request.get_type(), request, factory)
+        d = factory.connection
+        d.addCallback(sendHeaders, request)
+        def _(connection):
+            d = defer.Deferred()
+            connection.setResponseSink(d)
+            return d
+        d.addCallback(_)
+        def _(response):
+            try:
+                response = self.callMethod('response_%s' % response.status,
+                                           response, request)
+            except NoHandler:
+                response = self.callMethod('responseDefault', response, request)
+            return response
+        d.addCallback(_)
+        return d
+
+class BaseHandler:
+
+    def setOpener(self, parent):
+        self.parent = parent
+
+HTTP_PORT = 80
+HTTPS_PORT = 443
+
+class BaseHTTPHandler(BaseHandler):
+
+    def connect_http(self, request, factory):
+        from twisted.internet import reactor
+        host, port = urllib2.splitport(request.get_host())
+        if port is None:
+            port = HTTP_PORT
+        reactor.connectTCP(host, port, factory)
+        return 1 # handled
+
+    def connect_https(self, request, factory):
+        from twisted.internet import reactor
+        host, port = urllib2.splitport(request.get_host())
+        if port is None:
+            port = HTTPS_PORT
+        reactor.connectSSL(host, port, factory)
+        return 1 # handled
+
+    def responseDefault(self, response, request):
+        raise response
+
+    def response_200(self, response, request):
+        return response
+
+class HTTPRedirect(BaseHandler):
+
+    def response_301(self, response, req):
+        h = response.headers
+        url = (h.get('location') or h.get('uri') or [None])[0]
+        if url is None:
+            raise response
+        url = urlparse.urljoin(req.get_full_url(), url)
+        if len(req.visited)>10 or url in req.visited:
+            raise ValueError("redirection loop detected", url)
+        return self.parent.open(Request(url, req.get_data(),
+                                        req.headers, req.visited))
+
+    response_307 = response_302 = response_301
+
+    def response_303(self, response, req):
+        req.add_data(None)
+        return response_302(self, response, req)
+
+
+class ProxyHandler(BaseHandler):
+
+    def __init__(self, proxies=None):
+        if proxies is None:
+            proxies = urllib2.getproxies()
+        self.proxies = proxies
+
+    def connect_http(self, request, factory):
+        origType = request.get_type()
+        proxy = self.proxies.get(origType)
+        if not proxy:
+            return
+        type, url = urllib2.splittype(proxy)
+        host, _ = urllib2.splithost(url)
+        if '@' in host:
+            user, host = host.split('@', 1)
+            user = base64.encodestring(unquote(user_pass)).strip()
+            request.add_header('Proxy-Authorization', 'Basic '+user)
+        host = unquote(host)
+        request.set_proxy(host, type)
+        if origType == type:
+            return
+        else:
+            return self.callMethod('connect_'+request.type, request, factory)
+
+    connect_https = connect_http
+
+def opener():
+    return Opener(ProxyHandler(), BaseHTTPHandler(), HTTPRedirect())
+
+def read(response):
+    d, l = defer.Deferred(), []
+    response.setHandler(
+        dataReceived=l.append,
+        dataDone=lambda:d.callback(''.join(l)),
+        connectionLost=d.errback,
+    )
+    return d
+
+def download(fp):
+    def _(response):
+        d = defer.Deferred()
+        response.setHandler(
+            dataReceived=fp.write,
+            dataDone=lambda:d.callback(fp),
+            connectionLost=d.errback,
+        )
+        return d
+    return _
+
+def close(fp):
+    fp.close()
+
+def urlopen(url, data=None):
+    return opener().open(Request(url, data))
+
+def urlretrieve(name, url, data=None):
+    d = opener().open(Request(url, data))
+    d.addCallback(download(file(name, 'wb')))
+    d.addCallback(close)
+    return d
+
+if __name__ == '__main__':
+    from twisted.internet import reactor
+    import sys
+    opener().open(Request('http://www.yahoo.com/')
+    ).addCallback(download(sys.stdout))
+    reactor.run()