Update of /cvsroot/freevo/kaa/notifier/src
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv9774/notifier/src
Added Files:
.cvsignore __init__.py callback.py popen.py signals.py
thread.py
Log Message:
create notifier wrapper
--- NEW FILE: signals.py ---
# -*- coding: iso-8859-1 -*-
# -----------------------------------------------------------------------------
# signals.py - Signal handling for the notifier
# -----------------------------------------------------------------------------
# $Id: signals.py,v 1.1 2005/06/28 11:17:47 dischi Exp $
#
# -----------------------------------------------------------------------------
# kaa-notifier - Notifier Wrapper
# Copyright (C) 2005 Dirk Meyer, et al.
#
# First Version: Dirk Meyer <[EMAIL PROTECTED]>
# Maintainer: Dirk Meyer <[EMAIL PROTECTED]>
#
# Please see the file doc/AUTHORS for a complete list of authors.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MER-
# CHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
# Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# -----------------------------------------------------------------------------
from signal import *
import notifier
_signal_dict = {}
_signal_list = []
def register(sig, function):
"""
Register a signal handler.
"""
_signal_dict[sig] = function
signal(sig, _signal_catch)
def has_signal():
"""
Return True if there are signals in the queue.
"""
return _signal_list
def _signal_handler():
"""
Call all registered signal handler.
"""
while _signal_list:
sig = _signal_list.pop(0)
_signal_dict[sig](sig)
return False
def _signal_catch(sig, frame):
"""
Catch signals to be called from the main loop.
"""
if not sig in _signal_list:
# add catched signal to the list
_signal_list.append(sig)
# FIXME: let's hope this works because the handler
# is called asynchron
notifier.addTimer(0, _signal_handler)
return True
--- NEW FILE: .cvsignore ---
*.pyc *.pyo
--- NEW FILE: thread.py ---
# -*- coding: iso-8859-1 -*-
# -----------------------------------------------------------------------------
# thread.py - Thread module for the notifier
# -----------------------------------------------------------------------------
# $Id: thread.py,v 1.1 2005/06/28 11:17:47 dischi Exp $
#
# This module contains some wrapper classes for threading while running the
# notifier main loop. It should only be used when non blocking handling is not
# possible. The main loop itself is not thread save, the the function called in
# the thread should not touch any variables inside the application which are
# not protected by by a lock.
#
# You can create a Thread object with the function and it's
# arguments. After that you can call the start function to start the
# thread. This function has an optional parameter with a callback
# which will be called from the main loop once the thread is
# finished. The result of the thread function is the parameter for the
# callback.
#
# In most cases this module is not needed, please add a good reason why you
# wrap a function in a thread.
#
# If a thread needs to call a function from the main loop the helper function
# call_from_main can be used. It will schedule the function call in the main
# loop. It is not possible to get the return value of that call.
#
# -----------------------------------------------------------------------------
# kaa-notifier - Notifier Wrapper
# Copyright (C) 2005 Dirk Meyer, et al.
#
# First Version: Dirk Meyer <[EMAIL PROTECTED]>
# Maintainer: Dirk Meyer <[EMAIL PROTECTED]>
#
# Please see the file doc/AUTHORS for a complete list of authors.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MER-
# CHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
# Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# -----------------------------------------------------------------------------
__all__ = [ 'Thread', 'call_from_main' ]
# python imports
import copy
import threading
import notifier
import logging
# get logging object
log = logging.getLogger('fthread')
# internal list of callbacks that needs to be called from the main loop
_callbacks = []
# lock for adding / removing callbacks from _callbacks
_lock = threading.Lock()
class Thread(threading.Thread):
"""
Notifier aware wrapper for threads.
"""
def __init__(self, function, *args, **kargs):
threading.Thread.__init__(self)
self.callbacks = [ None, None ]
self.function = function
self.args = args
self.kargs = kargs
self.result = None
self.finished = False
self.exception = None
def start(self, callback=None, exception_callback = None):
"""
Start the thread.
"""
# append object to list of threads in watcher
_watcher.append(self)
# register callback
self.callbacks = [ callback, exception_callback ]
# start the thread
threading.Thread.start(self)
def run(self):
"""
Call the function and store the result
"""
try:
# run thread function
self.result = self.function(*self.args, **self.kargs)
except Exception, e:
log.exception('Thread crashed:')
self.exception = e
# set finished flag
self.finished = True
def callback(self):
"""
Run the callback.
"""
if self.exception and self.callbacks[1]:
self.callbacks[1](self.exception)
elif not self.exception and self.callbacks[0]:
self.callbacks[0](self.result)
def call_from_main(function, *args, **kwargs):
"""
Call a function from the main loop. The function isn't called when this
function is called, it is called when the watcher in the main loop is
called by the notifier.
"""
_lock.acquire()
_callbacks.append((function, args, kwargs))
_lock.release()
class Watcher(object):
"""
Watcher for running threads.
"""
def __init__(self):
self.__threads = []
self.__timer = None
def append(self, thread):
"""
Append a thread to the watcher.
"""
self.__threads.append(thread)
if not self.__timer:
self.__timer = notifier.addTimer( 10, self.check )
def check(self):
"""
Check for finished threads and callbacks that needs to be called from
the main loop.
"""
finished = []
# check if callbacks needs to be called from the main loop
if _callbacks:
# acquire lock
_lock.acquire()
# copy callback list
cb = copy.copy(_callbacks)
while _callbacks:
# delete callbacks
_callbacks.pop()
# release lock
_lock.release()
# call callback functions
for function, args, kwargs in cb:
function(*args, **kwargs)
for thread in self.__threads:
# check all threads
if thread.finished:
finished.append(thread)
if not finished:
# no finished thread, return
return True
# check all finished threads
for thread in finished:
# remove thread from list
self.__threads.remove(thread)
# call callback
thread.callback()
# join thread
thread.join()
if not self.__threads:
# remove watcher from notifier
self.__timer = None
return False
return True
# the global watcher object
_watcher = Watcher()
--- NEW FILE: popen.py ---
# -*- coding: iso-8859-1 -*-
# -----------------------------------------------------------------------------
# popen.py - process control using notifier
# -----------------------------------------------------------------------------
# $Id: popen.py,v 1.1 2005/06/28 11:17:47 dischi Exp $
#
# This module defines a process class similar to the once defined in popen2
# except that this class is aware of the notifier loop.
#
# When creating a Process object you can add files for logging the stdout and
# stderr of the process, you can send data to it and add a callback to be
# called when the process is dead. See the class member functions for more
# details.
#
# By inherting from the class you can also override the functions stdout_cb
# and stderr_cb to process stdout and stderr line by line.
#
# The killall function of this class can be called at the end of the programm
# to stop all running processes.
#
# -----------------------------------------------------------------------------
# kaa-notifier - Notifier Wrapper
# Copyright (C) 2005 Dirk Meyer, et al.
#
# First Version: Dirk Meyer <[EMAIL PROTECTED]>
# Maintainer: Dirk Meyer <[EMAIL PROTECTED]>
#
# Based on code by Krister Lagerstrom and Andreas Büsching
# Please see the file doc/AUTHORS for a complete list of authors.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MER-
# CHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
# Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# -----------------------------------------------------------------------------
__all__ = [ 'Process', 'killall' ]
# python imports
import os
import fcntl
import popen2
import glob
import re
import logging
# notifier
import notifier
# get logging object
log = logging.getLogger('popen')
class Process(object):
"""
Base class for started child processes
"""
def __init__( self, app, debugname = None, callback = None ):
"""
Init the child process 'app'. This can either be a string or a list
of arguments (similar to popen2). If debugname is given, the stdout
and stderr will also be written. To get the stdout and stderr, you
need to inherit from this class and override stdout_cb and stderr_cb.
If callback is set, the given function will be called after the child
is dead.
"""
if isinstance(app, str):
# app is a string to execute. It will be executed by 'sh -c '
# inside the popen code
self.binary = app.lstrip()
start_str = app
else:
# app is a list
while '' in app:
app.remove( '' )
self.binary = str( ' ' ).join( app )
start_str = app
self.__kill_timer = None
self.stopping = False
self.__dead = False
self.callback = callback
self.child = popen2.Popen3( start_str, True, 100 )
log.info('running %s (pid=%s)' % ( self.binary, self.child.pid ) )
# IO_Handler for stdout
self.stdout = IO_Handler( 'stdout', self.child.fromchild,
self.stdout_cb, debugname )
# IO_Handler for stderr
self.stderr = IO_Handler( 'stderr', self.child.childerr,
self.stderr_cb, debugname )
# add child to watcher
_watcher.append( self, self.__child_died )
def write( self, line ):
"""
Write a string to the app.
"""
try:
self.child.tochild.write(line)
self.child.tochild.flush()
except (IOError, ValueError):
pass
def is_alive( self ):
"""
Return True if the app is still running
"""
return not self.__dead
def stop( self, cmd = '', wait = True ):
"""
Stop the child. If 'cmd' is given, this stop command will send to
the app to stop itself. If this is not working, kill -15 and kill -9
will be used to kill the app. If wait is True, this function will
call notifier.step until the child is dead.
"""
if self.stopping:
return
self.stopping = True
if self.is_alive() and not self.__kill_timer:
if cmd:
log.info('sending exit command to app')
self.write(cmd)
cb = notifier.Callback( self.__kill, 15 )
self.__kill_timer = notifier.addTimer( 3000, cb )
else:
cb = notifier.Callback( self.__kill, 15 )
self.__kill_timer = notifier.addTimer( 0, cb )
while wait and not self.__dead:
notifier.step()
def __kill( self, signal ):
"""
Internal kill helper function
"""
if not self.is_alive():
self.__dead = True
return False
# child needs some assistance with dying ...
try:
os.kill( self.child.pid, signal )
except OSError:
pass
if signal == 15:
cb = notifier.Callback( self.__kill, 9 )
else:
cb = notifier.Callback( self.__killall, 15 )
self.__kill_timer = notifier.addTimer( 3000, cb )
return False
def __killall( self, signal ):
"""
Internal killall helper function
"""
if not self.is_alive():
self.__dead = True
return False
# child needs some assistance with dying ...
try:
# kill all applications with the string <appname> in their
# commandline. This implementation uses the /proc filesystem,
# it is Linux-dependent.
unify_name = re.compile('[^A-Za-z0-9]').sub
appname = unify_name('', self.binary)
cmdline_filenames = glob.glob('/proc/[0-9]*/cmdline')
for cmdline_filename in cmdline_filenames:
try:
fd = open(cmdline_filename)
cmdline = fd.read()
fd.close()
except IOError:
continue
if unify_name('', cmdline).find(appname) != -1:
# Found one, kill it
pid = int(cmdline_filename.split('/')[2])
try:
os.kill(pid, signal)
except:
pass
except OSError:
pass
log.info('kill -%d %s' % ( signal, self.binary ))
if signal == 15:
cb = notifier.Callback( self.__killall, 9 )
self.__kill_timer = notifier.addTimer( 2000, cb )
else:
log.critical('PANIC %s' % self.binary)
return False
def __child_died( self ):
"""
Callback from watcher when the child died.
"""
self.__dead = True
# close IO handler and kill timer
self.stdout.close()
self.stderr.close()
if self.__kill_timer:
notifier.removeTimer( self.__kill_timer )
if self.callback:
# call external callback on stop
self.callback()
def stdout_cb( self, line ):
"""
Override this method to receive stdout from the child app
The function receives complete lines
"""
pass
def stderr_cb( self, line ):
"""
Override this method to receive stderr from the child app
The function receives complete lines
"""
pass
class IO_Handler(object):
"""
Reading data from socket (stdout or stderr)
"""
def __init__( self, name, fp, callback, logger = None):
self.name = name
self.fp = fp
fcntl.fcntl( self.fp.fileno(), fcntl.F_SETFL, os.O_NONBLOCK )
self.callback = callback
self.logger = None
self.saved = ''
notifier.addSocket( fp, self._handle_input )
if logger:
logger = '%s-%s.log' % ( logger, name )
try:
try:
os.unlink(logger)
except:
pass
self.logger = open(logger, 'w')
log.info('logging child to "%s"' % logger)
except IOError:
log.warning('Error: Cannot open "%s" for logging' % logger)
def close( self ):
"""
Close the IO to the child.
"""
notifier.removeSocket( self.fp )
self.fp.close()
if self.logger:
self.logger.close()
self.logger = None
def _handle_input( self, socket ):
"""
Handle data input from socket.
"""
data = self.fp.read( 10000 )
if not data:
log.info('No data on %s for pid %s.' % ( self.name, os.getpid()))
notifier.removeSocket( self.fp )
self.fp.close()
if self.logger:
self.logger.close()
return False
data = data.replace('\r', '\n')
lines = data.split('\n')
# Only one partial line?
if len(lines) == 1:
self.saved += data
return True
# Combine saved data and first line, send to app
if self.logger:
self.logger.write( self.saved + lines[ 0 ] + '\n' )
self.callback( self.saved + lines[ 0 ] )
self.saved = ''
# There's one or more lines + possibly a partial line
if lines[ -1 ] != '':
# The last line is partial, save it for the next time
self.saved = lines[ -1 ]
# Send all lines except the last partial line to the app
for line in lines[ 1 : -1 ]:
if not line:
continue
if self.logger:
self.logger.write( line + '\n' )
self.callback( line )
else:
# Send all lines to the app
for line in lines[ 1 : ]:
if not line:
continue
if self.logger:
self.logger.write( line + '\n' )
self.callback( line )
return True
class Watcher(object):
def __init__( self ):
log.info('new process watcher instance')
self.__processes = {}
self.__timer = None
def append( self, proc, cb ):
self.__processes[ proc ] = cb
if not self.__timer:
log.info('start process watching')
self.__timer = notifier.addTimer(50, self.check)
def check( self ):
remove_proc = []
# check all processes
for p in self.__processes:
try:
if isinstance( p.child, popen2.Popen3 ):
pid, status = os.waitpid( p.child.pid, os.WNOHANG )
else:
pid, status = os.waitpid( p.pid, os.WNOHANG )
except OSError:
remove_proc.append( p )
continue
if not pid:
continue
log.info('Dead child: %s (%s)' % ( pid, status ))
if status == -1:
log.error('error retrieving process information from %d' % p)
elif os.WIFEXITED( status ) or os.WIFSIGNALED( status ) or \
os.WCOREDUMP( status ):
remove_proc.append( p )
# remove dead processes
for p in remove_proc:
if p in self.__processes:
# call stopped callback
self.__processes[ p ]()
del self.__processes[ p ]
# check if this function needs to be called again
if not self.__processes:
# no process left, remove timer
self.__timer = None
log.info('stop process watching')
return False
# return True to be called again
return True
def killall( self ):
# stop all childs without waiting
for p in self.__processes:
p.stop(wait=False)
# now wait until all childs are dead
while self.__processes:
notifier.step()
# global watcher instance
_watcher = Watcher()
# global killall function
killall = _watcher.killall
--- NEW FILE: __init__.py ---
# -*- coding: iso-8859-1 -*-
# -----------------------------------------------------------------------------
# __init__.py - Interface to kaa.notifier
# -----------------------------------------------------------------------------
# $Id: __init__.py,v 1.1 2005/06/28 11:17:47 dischi Exp $
#
# -----------------------------------------------------------------------------
# kaa-notifier - Notifier Wrapper
# Copyright (C) 2005 Dirk Meyer, et al.
#
# First Version: Dirk Meyer <[EMAIL PROTECTED]>
# Maintainer: Dirk Meyer <[EMAIL PROTECTED]>
#
# Please see the file doc/AUTHORS for a complete list of authors.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MER-
# CHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
# Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# -----------------------------------------------------------------------------
# python imports
import notifier
# the real notifier module
import logging
# kaa.notifier imports
from signals import *
from signals import register as signal
from popen import Process
from popen import killall as kill_processes
from thread import Thread, call_from_main
from callback import Callback, Function, CallbackObject
# get logging object
log = logging.getLogger('notifier')
# variable to check if the notifier is running
running = False
# variable to check if the notifier is initialized
initialized = False
def init(type = notifier.GENERIC):
"""
Init the notifier module.
"""
global initialized
initialized = True
notifier.init(type)
for var in globals():
if var in _notifier_vars:
globals()[var] = getattr(notifier, var)
def loop():
"""
Notifier main loop function. It will loop until an exception
is raised or sys.exit is called.
"""
global running
running = True
while 1:
try:
notifier.loop()
except KeyboardInterrupt:
break
except Exception, e:
if has_signal():
log.info('Call Signal Handler')
else:
running = False
raise e
running = False
def step(*args, **kwargs):
"""
Notifier step function with signal support.
"""
try:
notifier.step(*args, **kwargs)
except KeyboardInterrupt:
pass
except Exception, e:
if has_signal():
log.info('Call Signal Handler')
else:
raise e
def addTimer(interval, function, *args, **kwargs):
"""
The first argument specifies an interval in milliseconds, the
second argument a function. Optional parameters specify parameters
to the called function. This is function is called after interval
seconds. If it returns true it's called again after interval
seconds, otherwise it is removed from the scheduler.
This function returns an unique identifer which can be used to remove this
timer.
"""
if args or kwargs:
# create callback object to be passed to the notifier
function = Callback(function, *args, **kwargs)
return notifier.addTimer(interval, function)
def timer(interval, function, *args, **kwargs):
"""
The first argument specifies an interval in milliseconds, the
second argument a function. Optional parameters specify parameters
to the called function. This is function is called after interval
seconds. If it returns true it's called again after interval
seconds, otherwise it is removed from the scheduler.
This function returns a callback object with a remove function to remove
the timer from the notifier.
"""
t = CallbackObject(function, *args, **kwargs)
t.register('Timer', interval)
return t
# Import all notifier variables that are needed by the user of this
# module. Mark them in _notifier_vars to be overwritten later in init.
_notifier_vars = []
for var in dir(notifier):
if getattr(notifier, var) == None and not var in globals():
_notifier_vars.append(var)
exec('%s = notifier.%s' % (var, var))
if var == var.upper() and not var in _notifier_vars:
exec('%s = notifier.%s' % (var, var))
--- NEW FILE: callback.py ---
# -*- coding: iso-8859-1 -*-
# -----------------------------------------------------------------------------
# callback.py - Callback classes for the notifier
# -----------------------------------------------------------------------------
# $Id: callback.py,v 1.1 2005/06/28 11:17:47 dischi Exp $
#
# -----------------------------------------------------------------------------
# kaa-notifier - Notifier Wrapper
# Copyright (C) 2005 Dirk Meyer, et al.
#
# First Version: Dirk Meyer <[EMAIL PROTECTED]>
# Maintainer: Dirk Meyer <[EMAIL PROTECTED]>
#
# Please see the file doc/AUTHORS for a complete list of authors.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MER-
# CHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
# Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# -----------------------------------------------------------------------------
__all__ = [ 'Callback', 'Function', 'CallbackObject' ]
# import pyNotifier
import notifier
class Callback(object):
"""
Wrapper for functions calls with arguments inside the notifier. The
function passed to this objects get the parameter passed to this object
and after that the args and kwargs defined in the init function.
"""
def __init__(self, function, *args, **kwargs):
self.function = function
self.args = args
self.kwargs = kwargs
def __call__(self, *args):
"""
Call the callback function.
"""
return self.function(*(list(args) + list(self.args)), **self.kwargs)
class Function(Callback):
"""
Wrapper for functions calls with arguments inside the notifier. The
function passed to this objects get the only args and kwargs defined in
the init function, parameter passed to the object on call are dropped.
"""
def __call__(self, *args, **kwargs):
"""
Call the callback function.
"""
return self.function(*self.args, **self.kwargs)
class CallbackObject(Callback):
"""
Object to wrap notifier function calls with a remove function to remove
the timer / socket later. Do not create an object like this outside the
kaa.notifier source.
"""
def register(self, type, *args):
"""
Register the callback. Do not use this function directly.
"""
self.type = type
self.id = getattr(notifier, 'add' + type)(*(list(args) + [self]))
def remove(self):
"""
Remove the callback from the notifier.
"""
if not hasattr(self, 'id'):
raise AttributeError('Callback not registered')
getattr(notifier, 'remove' + self.type)(self.id)
-------------------------------------------------------
SF.Net email is sponsored by: Discover Easy Linux Migration Strategies
from IBM. Find simple to follow Roadmaps, straightforward articles,
informative Webcasts and more! Get everything you need to get up to
speed, fast. http://ads.osdn.com/?ad_id=7477&alloc_id=16492&op=click
_______________________________________________
Freevo-cvslog mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/freevo-cvslog