• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /macosx-10.10.1/pyobjc-45/2.6/pyobjc/pyobjc-core/Examples/NonFunctional/RemotePyInterpreter/
1__all__ = ['AsyncPythonInterpreter']
2
3try:
4    import fcntl
5except:
6    fcntl = None
7import os
8import sys
9import socket
10from StringIO import StringIO
11from netrepr import NetRepr, RemoteObjectPool, RemoteObjectReference
12import objc
13from Foundation import *
14
15IMPORT_MODULES = ['netrepr', 'remote_console', 'remote_pipe', 'remote_bootstrap']
16source = StringIO()
17for fn in IMPORT_MODULES:
18    for line in file(fn+'.py', 'rU'):
19        source.write(line)
20    source.write('\n\n')
21SOURCE = repr(source.getvalue()) + '\n'
22
23def bind_and_listen(hostport):
24    if isinstance(hostport, str):
25        host, port = hostport.split(':')
26        hostport = (host, int(port))
27    serversock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
28    # set close-on-exec
29    if hasattr(fcntl, 'FD_CLOEXEC'):
30        old = fcntl.fcntl(serversock.fileno(), fcntl.F_GETFD)
31        fcntl.fcntl(serversock.fileno(), fcntl.F_SETFD, old | fcntl.FD_CLOEXEC)
32    # allow the address to be re-used in a reasonable amount of time
33    if os.name == 'posix' and sys.platform != 'cygwin':
34        serversock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
35
36    serversock.bind(hostport)
37    serversock.listen(5)
38    return serversock
39
40class AsyncPythonInterpreter(NSObject):
41
42    commandReactor = objc.IBOutlet('commandReactor')
43
44    def init(self):
45        self = super(AsyncPythonInterpreter, self).init()
46        self.host = None
47        self.port = None
48        self.interpreterPath = None
49        self.scriptPath = None
50        self.commandReactor = None
51        self.serverSocket = None
52        self.serverFileHandle = None
53        self.buffer = ''
54        self.serverFileHandle = None
55        self.remoteFileHandle = None
56        self.childTask = None
57        return self
58
59    def initWithHost_port_interpreterPath_scriptPath_commandReactor_(self, host, port, interpreterPath, scriptPath, commandReactor):
60        self = self.init()
61        self.host = host
62        self.port = port
63        self.interpreterPath = interpreterPath
64        self.scriptPath = scriptPath
65        self.commandReactor = commandReactor
66        self.serverSocket = None
67        return self
68
69    def awakeFromNib(self):
70        defaults = NSUserDefaults.standardUserDefaults()
71        def default(k, v, typeCheck=None):
72            rval = defaults.objectForKey_(k)
73            if typeCheck is not None and rval is not None:
74                try:
75                    rval = typeCheck(rval)
76                except TypeError:
77                    NSLog(u'%s failed type check %s with value %s', k, typeCheck.__name__, rval)
78                    rval = None
79            if rval is None:
80                defaults.setObject_forKey_(v, k)
81                rval = v
82            return rval
83        self.host = default(u'AsyncPythonInterpreterInterpreterHost', u'127.0.0.1', str)
84        self.port = default(u'AsyncPythonInterpreterInterpreterPort', 0, int)
85        self.interpreterPath = default(u'AsyncPythonInterpreterInterpreterPath', u'/usr/bin/python', unicode)
86        self.scriptPath = type(self).bundleForClass().pathForResource_ofType_(u'tcpinterpreter', u'py')
87
88    def connect(self):
89        #NSLog(u'connect')
90        self.serverSocket = bind_and_listen((self.host, self.port))
91        self.serverFileHandle = NSFileHandle.alloc().initWithFileDescriptor_(self.serverSocket.fileno())
92        nc = NSNotificationCenter.defaultCenter()
93        nc.addObserver_selector_name_object_(
94            self,
95            'remoteSocketAccepted:',
96            NSFileHandleConnectionAcceptedNotification,
97            self.serverFileHandle)
98        self.serverFileHandle.acceptConnectionInBackgroundAndNotify()
99        self.remoteFileHandle = None
100        for k in os.environ.keys():
101            if k.startswith('PYTHON'):
102                del os.environ[k]
103        self.childTask = NSTask.launchedTaskWithLaunchPath_arguments_(self.interpreterPath, [self.scriptPath, repr(self.serverSocket.getsockname())])
104        nc.addObserver_selector_name_object_(
105            self,
106            'childTaskTerminated:',
107            NSTaskDidTerminateNotification,
108            self.childTask)
109        return self
110
111    def remoteSocketAccepted_(self, notification):
112        #NSLog(u'remoteSocketAccepted_')
113        self.serverFileHandle.closeFile()
114        self.serverFileHandle = None
115        ui = notification.userInfo()
116        self.remoteFileHandle = ui.objectForKey_(NSFileHandleNotificationFileHandleItem)
117        nc = NSNotificationCenter.defaultCenter()
118        nc.addObserver_selector_name_object_(
119            self,
120            'remoteFileHandleReadCompleted:',
121            NSFileHandleReadCompletionNotification,
122            self.remoteFileHandle)
123        self.writeBytes_(SOURCE)
124        self.remoteFileHandle.readInBackgroundAndNotify()
125        self.commandReactor.connectionEstablished_(self)
126        NSNotificationCenter.defaultCenter().postNotificationName_object_(u'AsyncPythonInterpreterOpened', self)
127
128    def remoteFileHandleReadCompleted_(self, notification):
129        #NSLog(u'remoteFileHandleReadCompleted_')
130        ui = notification.userInfo()
131        newData = ui.objectForKey_(NSFileHandleNotificationDataItem)
132        if newData is None:
133            self.close()
134            NSLog(u'Error: %@', ui.objectForKey_(NSFileHandleError))
135            return
136        bytes = newData.bytes()[:]
137        if len(bytes) == 0:
138            self.close()
139            return
140        self.remoteFileHandle.readInBackgroundAndNotify()
141        start = len(self.buffer)
142        buff = self.buffer + newData.bytes()[:]
143        #NSLog(u'current buffer: %s', buff)
144        lines = []
145        while True:
146            linebreak = buff.find('\n', start) + 1
147            if linebreak == 0:
148                break
149            lines.append(buff[:linebreak])
150            buff = buff[linebreak:]
151            start = 0
152        #NSLog(u'lines: %s', lines)
153        self.buffer = buff
154        for line in lines:
155            self.commandReactor.lineReceived_fromConnection_(line, self)
156
157    def writeBytes_(self, bytes):
158        #NSLog(u'Writing bytes: %s' bytes)
159        try:
160            self.remoteFileHandle.writeData_(NSData.dataWithBytes_length_(bytes, len(bytes)))
161        except objc.error:
162            self.close()
163        #NSLog(u'bytes written.')
164
165    def childTaskTerminated_(self, notification):
166        #NSLog(u'childTaskTerminated_')
167        self.close()
168
169    def closeServerFileHandle(self):
170        #NSLog(u'closeServerFileHandle')
171        if self.serverFileHandle is not None:
172            try:
173                self.serverFileHandle.closeFile()
174            except objc.error:
175                pass
176            self.serverFileHandle = None
177
178    def closeRemoteFileHandle(self):
179        #NSLog(u'closeRemoteFileHandle')
180        if self.remoteFileHandle is not None:
181            try:
182                self.remoteFileHandle.closeFile()
183            except objc.error:
184                pass
185            self.remoteFileHandle = None
186
187    def terminateChildTask(self):
188        #NSLog(u'terminateChildTask')
189        if self.childTask is not None:
190            try:
191                self.childTask.terminate()
192            except objc.error:
193                pass
194            self.childTask = None
195
196    def close(self):
197        #NSLog(u'close')
198        NSNotificationCenter.defaultCenter().removeObserver_(self)
199        self.finalClose()
200        NSNotificationCenter.defaultCenter().postNotificationName_object_(u'AsyncPythonInterpreterClosed', self)
201
202    def finalClose(self):
203        if self.commandReactor is not None:
204            self.commandReactor.connectionClosed_(self)
205            self.commandReactor = None
206        self.closeServerFileHandle()
207        self.closeRemoteFileHandle()
208        self.terminateChildTask()
209
210def test_console():
211    from PyObjCTools import AppHelper
212    from ConsoleReactor import ConsoleReactor
213    host = '127.0.0.1'
214    port = 0
215    interpreterPath = sys.executable
216    scriptPath = unicode(os.path.abspath('tcpinterpreter.py'))
217    commandReactor = ConsoleReactor.alloc().init()
218    interp = AsyncPythonInterpreter.alloc().initWithHost_port_interpreterPath_scriptPath_commandReactor_(host, port, interpreterPath, scriptPath, commandReactor)
219    interp.connect()
220    class ThisEventLoopStopper(NSObject):
221        def interpFinished_(self, notification):
222            AppHelper.stopEventLoop()
223    stopper = ThisEventLoopStopper.alloc().init()
224    NSNotificationCenter.defaultCenter().addObserver_selector_name_object_(stopper, 'interpFinished:', u'AsyncPythonInterpreterClosed', interp)
225    AppHelper.runConsoleEventLoop(installInterrupt=True)
226
227def main():
228    test_console()
229
230if __name__ == '__main__':
231    main()
232