Move IdleCommand from resolver.py to idlequeue.py

This commit is contained in:
Stephan Erb 2009-01-11 17:54:41 +00:00
parent 0f61260578
commit 541167aa3e
2 changed files with 126 additions and 120 deletions

View File

@ -20,14 +20,10 @@
import sys
import os
import re
import logging
log = logging.getLogger('gajim.c.resolver')
from xmpp.idlequeue import *
# needed for nslookup
if os.name == 'nt':
from subprocess import * # python24 only. we ask this for Windows
elif os.name == 'posix':
import fcntl
from xmpp.idlequeue import IdleCommand
# it is good to check validity of arguments, when calling system commands
ns_type_pattern = re.compile('^[a-z]+$')
@ -35,14 +31,13 @@ ns_type_pattern = re.compile('^[a-z]+$')
# match srv host_name
host_pattern = re.compile('^[a-z0-9\-._]*[a-z0-9]\.[a-z]{2,}$')
USE_LIBASYNCNS = False
try:
#raise ImportError("Manually disabled libasync")
import libasyncns
USE_LIBASYNCNS = True
log.info("libasyncns-python loaded")
except ImportError:
USE_LIBASYNCNS = False
log.debug("Import of libasyncns-python failed, getaddrinfo will block", exc_info=True)
# FIXME: Remove these prints before release, replace with a warning dialog.
@ -98,11 +93,11 @@ class CommonResolver():
def start_resolve(self, host, type):
pass
# FIXME: API usage is not consistent! This one requires that process is called
class LibAsyncNSResolver(CommonResolver):
'''
Asynchronous resolver using libasyncns-python. process() method has to be called
in order to proceed the pending requests.
Asynchronous resolver using libasyncns-python. process() method has to be
called in order to proceed the pending requests.
Based on patch submitted by Damien Thebault.
'''
def __init__(self):
@ -126,7 +121,6 @@ class LibAsyncNSResolver(CommonResolver):
CommonResolver._on_ready(self, host, type, result_list)
def process(self):
try:
self.asyncns.wait(False)
@ -157,6 +151,7 @@ class LibAsyncNSResolver(CommonResolver):
resq.userdata['callback'](resq.userdata['dname'], rl)
return True
class NSLookupResolver(CommonResolver):
'''
Asynchronous DNS resolver calling nslookup. Processing of pending requests
@ -264,112 +259,14 @@ class NSLookupResolver(CommonResolver):
result_list = self.parse_srv_result(host, result)
CommonResolver._on_ready(self, host, type, result_list)
def start_resolve(self, host, type):
''' spawn new nslookup process and start waiting for results '''
ns = NsLookup(self._on_ready, host, type)
ns.set_idlequeue(self.idlequeue)
ns.commandtimeout = 10
ns.start()
# TODO: move IdleCommand class in other file, maybe helpers ?
class IdleCommand(IdleObject):
def __init__(self, on_result):
# how long (sec.) to wait for result ( 0 - forever )
# it is a class var, instead of a constant and we can override it.
self.commandtimeout = 0
# when we have some kind of result (valid, ot not) we call this handler
self.result_handler = on_result
# if it is True, we can safetely execute the command
self.canexecute = True
self.idlequeue = None
self.result =''
def set_idlequeue(self, idlequeue):
self.idlequeue = idlequeue
def _return_result(self):
if self.result_handler:
self.result_handler(self.result)
self.result_handler = None
def _compose_command_args(self):
return ['echo', 'da']
def _compose_command_line(self):
''' return one line representation of command and its arguments '''
return reduce(lambda left, right: left + ' ' + right, self._compose_command_args())
def wait_child(self):
if self.pipe.poll() is None:
# result timeout
if self.endtime < self.idlequeue.current_time():
self._return_result()
self.pipe.stdout.close()
self.pipe.stdin.close()
else:
# child is still active, continue to wait
self.idlequeue.set_alarm(self.wait_child, 0.1)
else:
# child has quit
self.result = self.pipe.stdout.read()
self._return_result()
self.pipe.stdout.close()
self.pipe.stdin.close()
def start(self):
if not self.canexecute:
self.result = ''
self._return_result()
return
if os.name == 'nt':
self._start_nt()
elif os.name == 'posix':
self._start_posix()
def _start_nt(self):
# if gajim is started from noninteraactive shells stdin is closed and
# cannot be forwarded, so we have to keep it open
self.pipe = Popen(self._compose_command_args(), stdout=PIPE,
bufsize = 1024, shell = True, stderr = STDOUT, stdin = PIPE)
if self.commandtimeout >= 0:
self.endtime = self.idlequeue.current_time() + self.commandtimeout
self.idlequeue.set_alarm(self.wait_child, 0.1)
def _start_posix(self):
self.pipe = os.popen(self._compose_command_line())
self.fd = self.pipe.fileno()
fcntl.fcntl(self.pipe, fcntl.F_SETFL, os.O_NONBLOCK)
self.idlequeue.plug_idle(self, False, True)
if self.commandtimeout >= 0:
self.idlequeue.set_read_timeout(self.fd, self.commandtimeout)
def end(self):
self.idlequeue.unplug_idle(self.fd)
try:
self.pipe.close()
except:
pass
def pollend(self):
self.idlequeue.remove_timeout(self.fd)
self.end()
self._return_result()
def pollin(self):
try:
res = self.pipe.read()
except Exception, e:
res = ''
if res == '':
return self.pollend()
else:
self.result += res
def read_timeout(self):
self.end()
self._return_result()
class NsLookup(IdleCommand):
def __init__(self, on_result, host='_xmpp-client', type='srv'):
IdleCommand.__init__(self, on_result)
@ -378,11 +275,11 @@ class NsLookup(IdleCommand):
self.type = type.lower()
if not host_pattern.match(self.host):
# invalid host name
print >> sys.stderr, 'Invalid host: %s' % self.host
log.error('Invalid host: %s' % self.host)
self.canexecute = False
return
if not ns_type_pattern.match(self.type):
print >> sys.stderr, 'Invalid querytype: %s' % self.type
log.error('Invalid querytype: %s' % self.type)
self.canexecute = False
return
@ -396,15 +293,12 @@ class NsLookup(IdleCommand):
# below lines is on how to use API and assist in testing
if __name__ == '__main__':
if os.name == 'posix':
idlequeue = IdleQueue()
elif os.name == 'nt':
idlequeue = SelectIdleQueue()
# testing Resolver class
import gobject
import gtk
from xmpp import idlequeue
resolver = Resolver(idlequeue)
idlequeue = idlequeue.get_idlequeue()
resolver = get_resolver(idlequeue)
def clicked(widget):
global resolver

View File

@ -20,12 +20,19 @@ import os
import select
import logging
log = logging.getLogger('gajim.c.x.idlequeue')
# needed for get_idleqeue
try:
import gobject
HAVE_GOBJECT = True
except ImportError:
HAVE_GOBJECT = False
# needed for idlecommand
if os.name == 'nt':
from subprocess import * # python24 only. we ask this for Windows
elif os.name == 'posix':
import fcntl
FLAG_WRITE = 20 # write only
FLAG_READ = 19 # read only
@ -36,6 +43,7 @@ PENDING_READ = 3 # waiting read event
PENDING_WRITE = 4 # waiting write event
IS_CLOSED = 16 # channel closed
def get_idlequeue():
''' Get an appropriate idlequeue '''
if os.name == 'nt':
@ -74,6 +82,110 @@ class IdleObject:
pass
class IdleCommand(IdleObject):
'''
Can be subclassed to execute commands asynchronously by the idlequeue.
Result will be optained via file descriptor of created pipe
'''
def __init__(self, on_result):
IdleObject.__init__(self)
# how long (sec.) to wait for result ( 0 - forever )
# it is a class var, instead of a constant and we can override it.
self.commandtimeout = 0
# when we have some kind of result (valid, ot not) we call this handler
self.result_handler = on_result
# if it is True, we can safetely execute the command
self.canexecute = True
self.idlequeue = None
self.result =''
def set_idlequeue(self, idlequeue):
self.idlequeue = idlequeue
def _return_result(self):
if self.result_handler:
self.result_handler(self.result)
self.result_handler = None
def _compose_command_args(self):
return ['echo', 'da']
def _compose_command_line(self):
''' return one line representation of command and its arguments '''
return reduce(lambda left, right: left + ' ' + right,
self._compose_command_args())
def wait_child(self):
if self.pipe.poll() is None:
# result timeout
if self.endtime < self.idlequeue.current_time():
self._return_result()
self.pipe.stdout.close()
self.pipe.stdin.close()
else:
# child is still active, continue to wait
self.idlequeue.set_alarm(self.wait_child, 0.1)
else:
# child has quit
self.result = self.pipe.stdout.read()
self._return_result()
self.pipe.stdout.close()
self.pipe.stdin.close()
def start(self):
if not self.canexecute:
self.result = ''
self._return_result()
return
if os.name == 'nt':
self._start_nt()
elif os.name == 'posix':
self._start_posix()
def _start_nt(self):
# if gajim is started from noninteraactive shells stdin is closed and
# cannot be forwarded, so we have to keep it open
self.pipe = Popen(self._compose_command_args(), stdout=PIPE,
bufsize = 1024, shell = True, stderr = STDOUT, stdin = PIPE)
if self.commandtimeout >= 0:
self.endtime = self.idlequeue.current_time() + self.commandtimeout
self.idlequeue.set_alarm(self.wait_child, 0.1)
def _start_posix(self):
self.pipe = os.popen(self._compose_command_line())
self.fd = self.pipe.fileno()
fcntl.fcntl(self.pipe, fcntl.F_SETFL, os.O_NONBLOCK)
self.idlequeue.plug_idle(self, False, True)
if self.commandtimeout >= 0:
self.idlequeue.set_read_timeout(self.fd, self.commandtimeout)
def end(self):
self.idlequeue.unplug_idle(self.fd)
try:
self.pipe.close()
except:
pass
def pollend(self):
self.idlequeue.remove_timeout(self.fd)
self.end()
self._return_result()
def pollin(self):
try:
res = self.pipe.read()
except Exception, e:
res = ''
if res == '':
return self.pollend()
else:
self.result += res
def read_timeout(self):
self.end()
self._return_result()
class IdleQueue:
'''
IdleQueue provide three distinct time based features. Uses select.poll()