python interactive - threaded console (run -i)

C

castironpi

#interactive run -i
import threading
import Queue
import functools
class GenThread( threading.Thread, Queue.Queue ):
_taskid= 0
def __init__( self, *ar, **kw ):
threading.Thread.__init__( self, *ar, **kw )
Queue.Queue.__init__( self )
self.setDaemon( True )
self.start()
self._doneevents= {}
def run( self ):
while 1:
id, fun, ar, kw= self.get()
self._= fun( *ar, **kw )
self._doneevents[id].set()
if self._:
self._print( self._ )
def me( self, fun, *ar, **kw ):
id= GenThread._taskid
GenThread._taskid+= 1
self.put( ( id, fun, ar, kw ) )
self._doneevents[ id ]= e= threading.Event()
return e
def _print( self, *ar, **kw ):
print( self.getName(), *ar, **kw )

for i in range( 10 ):
exec( 'th%i= GenThread()'% i )

import socket
host= socket.socket( socket.AF_INET,
socket.SOCK_STREAM )
host.bind( ( '', 8000 ) )
th1.me( host.listen, 1 )
_acc= th1.me( host.accept )
cli= socket.socket( socket.AF_INET,
socket.SOCK_STREAM )
th2.me( cli.connect, ( 'localhost', 8000 ) )
_acc.wait()
conn= th1._[0]

ConnClose= object()
class IncompleteTransmission( Exception ): pass
def _draintilclose( conn, understandfun= None ):
while 1:
_preamb= ''
while 1:
_data= conn.recv( 1 )
if not _data:
conn.close()
return ConnClose
if _data== b'\x00':
break
_preamb+= _data.decode()
_len= int( _preamb, 16 )
_lenleft= _len
_curmsg= bytearray()
while _lenleft:
_data= conn.recv( min( 4096, _lenleft ) )
if not _data:
raise IncompleteTransmission
_curmsg.extend( _data )
_lenleft-= len( _data )
assert len( _curmsg )== _len
if None is not understandfun:
understandfun( _curmsg )

def _dressandsend( sck, msg ):
_preamble= hex( len( msg ) )[2:]+ '\x00'
_dressed= bytes( _preamble, 'ascii' )+ msg
_lenleft= len( _dressed )
while _lenleft:
_sent= sck.send( _dressed[-_lenleft:] )
_lenleft-= _sent

th1.me( _draintilclose, conn, th1._print )
th2.me( _draintilclose, cli, th2._print )

This is cool! Set up a socket and listen 'til close on one thread,
print output by default as complete messages are received. Listen
'til close, same, on another, th1, th2 respectively. Roughly off the
console:
Thread-1 bytearray(b'def')

Rearranged the '>>>'. It's damn annoying. Probably a specialized
stdout to detect when in a prompt and add extra newline, maybe
modified GenThread._print. Someone go replace hex with base255 and
conn.recv( 1 ) with conn.recv( 2 ). Should take any length-- base256
over hex is just a factor of 2, and still O( log( message length ) ).
['ConnClose', 'GenThread', 'IncompleteTransmission', 'Queue',
'__builtins__', '__doc__', '__name__', '__package__', '_acc',
'_draintilclose', '_dressandsend', 'cli', 'conn', 'functools', 'host',
'i', 'socket', 'th1', 'th2', 'th3', 'th4', 'th5', 'th6', 'th7', 'th8',
'th9', 'threading']
 
C

castironpi

        self._doneevents= {}

is extraneous.
id, fun, ar, kw= self.get()
done, fun, ar, kw= self.get()

suffices.
def _draintilclose( conn, understandfun= None ):
        if None is not understandfun:
            understandfun( _curmsg )

yield _curmsg

is an interesting synonym: the static interface. Compare signatures:

def _draingen( conn ):
...
yield _curmsg

def _draintilclose( sck, th ):
for x in _draingen( sck ):
th._print( x )

or in place of th._print, whatever function you passed to
understandfun.

The general case, pseudocode, consumer:

def _draintilclose( conn, visitee ):
for x in _draingen( conn ):
x.visit( visitee )

class Tool1Visitee:
def typeA( self, arg ):
tool1.listbox.add( arg )
def typeB( self, arg ):
tool1.combobox.add( arg )
class Tool2Visitee:
def typeA( self, arg ):
tool2.icons.add( arg )
def typeB( self, arg ):
tool2.listbox.add( arg )

and:

class VisitorX:
def visit( self, v ):
v.typeA( self._attr ):
class VisitorY:
def visit( self, v ):
v.typeB( self._attr ):

Producer:

def _draingen( conn ):
...
if:
_curvisitee= VisitorX( _curmsg )
else:
_curvisitee= VisitorY( _curmsg )
yield _curvisitee

and:

th1Visitee= Tool1Visitee()
new_thread( _draintilclose, conn1, th1Visitee )
th2Visitee= Tool2Visitee()
new_thread( _draintilclose, conn2, th2Visitee )

Tool1Visitor.typeA still just receives a byte array, but type
information comes with. By that point, the 'message knows' that it's
in a Tool1Visitor, and that it's a typeA message, and so adds it to
tool1.listbox. That puts just the right information in just the right
place... if it's the right time. Or pass the visitee directly to
_draingen as fit, without the visitor or yields, as a callback
collection.

def _draingen( conn, visitee ):
...
if:
visitee.typeA( _curmsg )
else:
visitee.typeB( _curmsg )


As a callback collection:

tool1_ft= dict( typeA= tool1.listbox.add, typeB=
tool1.combobox.add )
tool2_ft= dict( typeA= tool2.icons.add, typeB= too2.listbox.add )
@new_threads( conn1, tool1_ft )
@new_threads( conn2, tool2_ft )
def _( conn, tool_ft ):
for tp, msg in _draingen( conn, tool_ft ):
tool_ft[ tp ]( msg )

using a generator, or directly without:

tool1_ft= dict( typeA= tool1.listbox.add, typeB=
tool1.combobox.add )
new_thread( _draintilclose, conn1, tool1_ft )
tool2_ft= dict( typeA= tool2.icons.add, typeB= too2.listbox.add )
new_thread( _draintilclose, conn2, tool2_ft )
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,007
Latest member
obedient dusk

Latest Threads

Top