Merge pull request #3 from areski/master

PEP8 Fixes
develop
Randall Degges 2012-11-11 20:01:39 -08:00
commit 2664927f02
6 changed files with 286 additions and 190 deletions

View File

@ -1,21 +1,25 @@
2011-05-31 Randall Degges <rdegges@gmail.com
2012-11-11 Arezqui Belaid <areski@gmail.com>
* PEP8 Fixes
2011-05-31 Randall Degges <rdegges@gmail.com>
* BUGFIX: Fixing issue that prevented manager.status command from returning * BUGFIX: Fixing issue that prevented manager.status command from returning
proper output. proper output.
2007-01-26 Matthew Nicholson <mnicholson@digium.com> 2007-01-26 Matthew Nicholson <mnicholson@digium.com>
* asterisk/manager.py: Make get_header() functions work like * asterisk/manager.py: Make get_header() functions work like
dict.get(). dict.get().
* UPGRADE: Updated. * UPGRADE: Updated.
2007-01-16 Matthew Nicholson <mnicholson@digium.com> 2007-01-16 Matthew Nicholson <mnicholson@digium.com>
* asterisk/manager.py: Fix support for Manager.command(). Patch from * asterisk/manager.py: Fix support for Manager.command(). Patch from
Karl Putland <karl@klasstek.com>. Karl Putland <karl@klasstek.com>.
2007-01-02 Matthew Nicholson <mnicholson@digium.com> 2007-01-02 Matthew Nicholson <mnicholson@digium.com>
* asterisk/agi.py (AGI.set_autohangup): Fixed syntax error. * asterisk/agi.py (AGI.set_autohangup): Fixed syntax error.
2006-11-28 Matthew Nicholson <mnicholson@digium.com> 2006-11-28 Matthew Nicholson <mnicholson@digium.com>
@ -94,4 +98,3 @@
* ChangeLog: Added. * ChangeLog: Added.
* TODO: Added. * TODO: Added.
* MANIFEST.in: Added ChangeLog and TODO. * MANIFEST.in: Added ChangeLog and TODO.

View File

@ -19,34 +19,65 @@ pyvr
""" """
import sys, pprint, re import sys
import pprint
import re
from types import ListType from types import ListType
import signal import signal
DEFAULT_TIMEOUT = 2000 # 2sec timeout used as default for functions that take timeouts DEFAULT_TIMEOUT = 2000 # 2sec timeout used as default for functions that take timeouts
DEFAULT_RECORD = 20000 # 20sec record time DEFAULT_RECORD = 20000 # 20sec record time
re_code = re.compile(r'(^\d*)\s*(.*)') re_code = re.compile(r'(^\d*)\s*(.*)')
re_kv = re.compile(r'(?P<key>\w+)=(?P<value>[^\s]+)\s*(?:\((?P<data>.*)\))*') re_kv = re.compile(r'(?P<key>\w+)=(?P<value>[^\s]+)\s*(?:\((?P<data>.*)\))*')
class AGIException(Exception): pass
class AGIError(AGIException): pass
class AGIUnknownError(AGIError): pass class AGIException(Exception):
pass
class AGIAppError(AGIError): pass
class AGIError(AGIException):
pass
class AGIUnknownError(AGIError):
pass
class AGIAppError(AGIError):
pass
# there are several different types of hangups we can detect # there are several different types of hangups we can detect
# they all are derrived from AGIHangup # they all are derrived from AGIHangup
class AGIHangup(AGIAppError): pass
class AGISIGHUPHangup(AGIHangup): pass
class AGISIGPIPEHangup(AGIHangup): pass
class AGIResultHangup(AGIHangup): pass
class AGIDBError(AGIAppError): pass
class AGIUsageError(AGIError): pass class AGIHangup(AGIAppError):
class AGIInvalidCommand(AGIError): pass pass
class AGISIGHUPHangup(AGIHangup):
pass
class AGISIGPIPEHangup(AGIHangup):
pass
class AGIResultHangup(AGIHangup):
pass
class AGIDBError(AGIAppError):
pass
class AGIUsageError(AGIError):
pass
class AGIInvalidCommand(AGIError):
pass
class AGI: class AGI:
""" """
@ -73,10 +104,10 @@ class AGI:
if line == '': if line == '':
#blank line signals end #blank line signals end
break break
key,data = line.split(':')[0], ':'.join(line.split(':')[1:]) key, data = line.split(':')[0], ':'.join(line.split(':')[1:])
key = key.strip() key = key.strip()
data = data.strip() data = data.strip()
if key <> '': if key != '':
self.env[key] = data self.env[key] = data
sys.stderr.write('class AGI: self.env = ') sys.stderr.write('class AGI: self.env = ')
sys.stderr.write(pprint.pformat(self.env)) sys.stderr.write(pprint.pformat(self.env))
@ -92,7 +123,7 @@ class AGI:
def test_hangup(self): def test_hangup(self):
"""This function throws AGIHangup if we have recieved a SIGHUP""" """This function throws AGIHangup if we have recieved a SIGHUP"""
if self._got_sighup: if self._got_sighup:
raise AGISIGHUPHangup("Received SIGHUP from Asterisk") raise AGISIGHUPHangup("Received SIGHUP from Asterisk")
def execute(self, command, *args): def execute(self, command, *args):
self.test_hangup() self.test_hangup()
@ -100,7 +131,7 @@ class AGI:
try: try:
self.send_command(command, *args) self.send_command(command, *args)
return self.get_result() return self.get_result()
except IOError,e: except IOError, e:
if e.errno == 32: if e.errno == 32:
# Broken Pipe * let us go # Broken Pipe * let us go
raise AGISIGPIPEHangup("Received SIGPIPE") raise AGISIGPIPEHangup("Received SIGPIPE")
@ -110,7 +141,7 @@ class AGI:
def send_command(self, command, *args): def send_command(self, command, *args):
"""Send a command to Asterisk""" """Send a command to Asterisk"""
command = command.strip() command = command.strip()
command = '%s %s' % (command, ' '.join(map(str,args))) command = '%s %s' % (command, ' '.join(map(str, args)))
command = command.strip() command = command.strip()
if command[-1] != '\n': if command[-1] != '\n':
command += '\n' command += '\n'
@ -121,7 +152,7 @@ class AGI:
def get_result(self, stdin=sys.stdin): def get_result(self, stdin=sys.stdin):
"""Read the result of a command from Asterisk""" """Read the result of a command from Asterisk"""
code = 0 code = 0
result = {'result':('','')} result = {'result': ('', '')}
line = stdin.readline().strip() line = stdin.readline().strip()
sys.stderr.write(' RESULT_LINE: %s\n' % line) sys.stderr.write(' RESULT_LINE: %s\n' % line)
m = re_code.search(line) m = re_code.search(line)
@ -130,7 +161,7 @@ class AGI:
code = int(code) code = int(code)
if code == 200: if code == 200:
for key,value,data in re_kv.findall(response): for key, value, data in re_kv.findall(response):
result[key] = (value, data) result[key] = (value, data)
# If user hangs up... we get 'hangup' in the data # If user hangs up... we get 'hangup' in the data
@ -226,7 +257,8 @@ class AGI:
extension must not be included in the filename. extension must not be included in the filename.
""" """
escape_digits = self._process_digit_list(escape_digits) escape_digits = self._process_digit_list(escape_digits)
response = self.execute('STREAM FILE', filename, escape_digits, sample_offset) response = self.execute(
'STREAM FILE', filename, escape_digits, sample_offset)
res = response['result'][0] res = response['result'][0]
if res == '0': if res == '0':
return '' return ''
@ -265,7 +297,8 @@ class AGI:
""" """
res = self.execute('SEND IMAGE', filename)['result'][0] res = self.execute('SEND IMAGE', filename)['result'][0]
if res != '0': if res != '0':
raise AGIAppError('Channel falure on channel %s' % self.env.get('agi_channel','UNKNOWN')) raise AGIAppError('Channel falure on channel %s' %
self.env.get('agi_channel', 'UNKNOWN'))
def say_digits(self, digits, escape_digits=''): def say_digits(self, digits, escape_digits=''):
"""agi.say_digits(digits, escape_digits='') --> digit """agi.say_digits(digits, escape_digits='') --> digit
@ -326,7 +359,8 @@ class AGI:
""" """
characters = self._process_digit_list(characters) characters = self._process_digit_list(characters)
escape_digits = self._process_digit_list(escape_digits) escape_digits = self._process_digit_list(escape_digits)
res = self.execute('SAY PHONETIC', characters, escape_digits)['result'][0] res = self.execute(
'SAY PHONETIC', characters, escape_digits)['result'][0]
if res == '0': if res == '0':
return '' return ''
else: else:
@ -372,8 +406,10 @@ class AGI:
in seconds since the UNIX Epoch (Jan 1, 1970 00:00:00). in seconds since the UNIX Epoch (Jan 1, 1970 00:00:00).
""" """
escape_digits = self._process_digit_list(escape_digits) escape_digits = self._process_digit_list(escape_digits)
if format: format = self._quote(format) if format:
res = self.execute('SAY DATETIME', seconds, escape_digits, format, zone)['result'][0] format = self._quote(format)
res = self.execute(
'SAY DATETIME', seconds, escape_digits, format, zone)['result'][0]
if res == '0': if res == '0':
return '' return ''
else: else:
@ -401,7 +437,8 @@ class AGI:
""" """
escape_digits = self._process_digit_list(escape_digits) escape_digits = self._process_digit_list(escape_digits)
if timeout: if timeout:
response = self.execute('GET OPTION', filename, escape_digits, timeout) response = self.execute(
'GET OPTION', filename, escape_digits, timeout)
else: else:
response = self.execute('GET OPTION', filename, escape_digits) response = self.execute('GET OPTION', filename, escape_digits)
@ -455,7 +492,8 @@ class AGI:
exceeding the end of the file exceeding the end of the file
""" """
escape_digits = self._process_digit_list(escape_digits) escape_digits = self._process_digit_list(escape_digits)
res = self.execute('RECORD FILE', self._quote(filename), format, escape_digits, timeout, offset, beep)['result'][0] res = self.execute('RECORD FILE', self._quote(filename), format,
escape_digits, timeout, offset, beep)['result'][0]
try: try:
return chr(int(res)) return chr(int(res))
except: except:
@ -510,11 +548,11 @@ class AGI:
7 Line is busy 7 Line is busy
""" """
try: try:
result = self.execute('CHANNEL STATUS', channel) result = self.execute('CHANNEL STATUS', channel)
except AGIHangup: except AGIHangup:
raise raise
except AGIAppError: except AGIAppError:
result = {'result': ('-1','')} result = {'result': ('-1', '')}
return int(result['result'][0]) return int(result['result'][0])
@ -530,27 +568,28 @@ class AGI:
the variable is not set, an empty string is returned. the variable is not set, an empty string is returned.
""" """
try: try:
result = self.execute('GET VARIABLE', self._quote(name)) result = self.execute('GET VARIABLE', self._quote(name))
except AGIResultHangup: except AGIResultHangup:
result = {'result': ('1', 'hangup')} result = {'result': ('1', 'hangup')}
res, value = result['result'] res, value = result['result']
return value return value
def get_full_variable(self, name, channel = None): def get_full_variable(self, name, channel=None):
"""Get a channel variable. """Get a channel variable.
This function returns the value of the indicated channel variable. If This function returns the value of the indicated channel variable. If
the variable is not set, an empty string is returned. the variable is not set, an empty string is returned.
""" """
try: try:
if channel: if channel:
result = self.execute('GET FULL VARIABLE', self._quote(name), self._quote(channel)) result = self.execute('GET FULL VARIABLE',
else: self._quote(name), self._quote(channel))
result = self.execute('GET FULL VARIABLE', self._quote(name)) else:
result = self.execute('GET FULL VARIABLE', self._quote(name))
except AGIResultHangup: except AGIResultHangup:
result = {'result': ('1', 'hangup')} result = {'result': ('1', 'hangup')}
res, value = result['result'] res, value = result['result']
return value return value
@ -571,10 +610,12 @@ class AGI:
""" """
family = '"%s"' % family family = '"%s"' % family
key = '"%s"' % key key = '"%s"' % key
result = self.execute('DATABASE GET', self._quote(family), self._quote(key)) result = self.execute(
'DATABASE GET', self._quote(family), self._quote(key))
res, value = result['result'] res, value = result['result']
if res == '0': if res == '0':
raise AGIDBError('Key not found in database: family=%s, key=%s' % (family, key)) raise AGIDBError('Key not found in database: family=%s, key=%s' %
(family, key))
elif res == '1': elif res == '1':
return value return value
else: else:
@ -585,7 +626,8 @@ class AGI:
Adds or updates an entry in the Asterisk database for a Adds or updates an entry in the Asterisk database for a
given family, key, and value. given family, key, and value.
""" """
result = self.execute('DATABASE PUT', self._quote(family), self._quote(key), self._quote(value)) result = self.execute('DATABASE PUT', self._quote(
family), self._quote(key), self._quote(value))
res, value = result['result'] res, value = result['result']
if res == '0': if res == '0':
raise AGIDBError('Unable to put vaule in databale: family=%s, key=%s, value=%s' % (family, key, value)) raise AGIDBError('Unable to put vaule in databale: family=%s, key=%s, value=%s' % (family, key, value))
@ -595,7 +637,8 @@ class AGI:
Deletes an entry in the Asterisk database for a Deletes an entry in the Asterisk database for a
given family and key. given family and key.
""" """
result = self.execute('DATABASE DEL', self._quote(family), self._quote(key)) result = self.execute(
'DATABASE DEL', self._quote(family), self._quote(key))
res, value = result['result'] res, value = result['result']
if res == '0': if res == '0':
raise AGIDBError('Unable to delete from database: family=%s, key=%s' % (family, key)) raise AGIDBError('Unable to delete from database: family=%s, key=%s' % (family, key))
@ -605,7 +648,8 @@ class AGI:
Deletes a family or specific keytree with in a family Deletes a family or specific keytree with in a family
in the Asterisk database. in the Asterisk database.
""" """
result = self.execute('DATABASE DELTREE', self._quote(family), self._quote(key)) result = self.execute(
'DATABASE DELTREE', self._quote(family), self._quote(key))
res, value = result['result'] res, value = result['result']
if res == '0': if res == '0':
raise AGIDBError('Unable to delete tree from database: family=%s, key=%s' % (family, key)) raise AGIDBError('Unable to delete tree from database: family=%s, key=%s' % (family, key))
@ -616,7 +660,7 @@ class AGI:
""" """
self.execute('NOOP') self.execute('NOOP')
if __name__=='__main__': if __name__ == '__main__':
agi = AGI() agi = AGI()
#agi.appexec('festival','Welcome to Klass Technologies. Thank you for calling.') #agi.appexec('festival','Welcome to Klass Technologies. Thank you for calling.')
#agi.appexec('festival','This is a test of the text to speech engine.') #agi.appexec('festival','This is a test of the text to speech engine.')
@ -638,11 +682,12 @@ if __name__=='__main__':
#agi.appexec('background','demo-congrats') #agi.appexec('background','demo-congrats')
try: try:
agi.appexec('backgrounder','demo-congrats') agi.appexec('backgrounder', 'demo-congrats')
except AGIAppError: except AGIAppError:
sys.stderr.write("Handled exception for missing application backgrounder\n") sys.stderr.write(
"Handled exception for missing application backgrounder\n")
agi.set_variable('foo','bar') agi.set_variable('foo', 'bar')
agi.get_variable('foo') agi.get_variable('foo')
try: try:
@ -661,6 +706,7 @@ if __name__=='__main__':
agi.database_del('foo', 'bar') agi.database_del('foo', 'bar')
agi.database_deltree('foo') agi.database_deltree('foo')
except AGIDBError: except AGIDBError:
sys.stderr.write("Handled exception for missing database entry bar:foo\n") sys.stderr.write(
"Handled exception for missing database entry bar:foo\n")
agi.hangup() agi.hangup()

View File

@ -36,6 +36,7 @@ import sys
__UNDEF__ = [] # a special sentinel object __UNDEF__ = [] # a special sentinel object
def lookup(name, frame, locals): def lookup(name, frame, locals):
"""Find the value for a given name in the given environment.""" """Find the value for a given name in the given environment."""
if name in locals: if name in locals:
@ -44,7 +45,7 @@ def lookup(name, frame, locals):
return 'global', frame.f_globals[name] return 'global', frame.f_globals[name]
if '__builtins__' in frame.f_globals: if '__builtins__' in frame.f_globals:
builtins = frame.f_globals['__builtins__'] builtins = frame.f_globals['__builtins__']
if type(builtins) is type({}): if isinstance(builtins, type({})):
if name in builtins: if name in builtins:
return 'builtin', builtins[name] return 'builtin', builtins[name]
else: else:
@ -52,12 +53,15 @@ def lookup(name, frame, locals):
return 'builtin', getattr(builtins, name) return 'builtin', getattr(builtins, name)
return None, __UNDEF__ return None, __UNDEF__
def scanvars(reader, frame, locals): def scanvars(reader, frame, locals):
"""Scan one logical line of Python and look up values of variables used.""" """Scan one logical line of Python and look up values of variables used."""
import tokenize, keyword import tokenize
import keyword
vars, lasttoken, parent, prefix, value = [], None, None, '', __UNDEF__ vars, lasttoken, parent, prefix, value = [], None, None, '', __UNDEF__
for ttype, token, start, end, line in tokenize.generate_tokens(reader): for ttype, token, start, end, line in tokenize.generate_tokens(reader):
if ttype == tokenize.NEWLINE: break if ttype == tokenize.NEWLINE:
break
if ttype == tokenize.NAME and token not in keyword.kwlist: if ttype == tokenize.NAME and token not in keyword.kwlist:
if lasttoken == '.': if lasttoken == '.':
if parent is not __UNDEF__: if parent is not __UNDEF__:
@ -77,9 +81,15 @@ def scanvars(reader, frame, locals):
def text((etype, evalue, etb), context=5): def text((etype, evalue, etb), context=5):
"""Return a plain text document describing a given traceback.""" """Return a plain text document describing a given traceback."""
import os, types, time, traceback, linecache, inspect, pydoc import os
import types
import time
import traceback
import linecache
import inspect
import pydoc
if type(etype) is types.ClassType: if isinstance(etype, types.ClassType):
etype = etype.__name__ etype = etype.__name__
pyver = 'Python ' + sys.version.split()[0] + ': ' + sys.executable pyver = 'Python ' + sys.version.split()[0] + ': ' + sys.executable
date = time.ctime(time.time()) date = time.ctime(time.time())
@ -97,13 +107,16 @@ function calls leading up to the error, in the order they occurred.
if func != '?': if func != '?':
call = 'in ' + func + \ call = 'in ' + func + \
inspect.formatargvalues(args, varargs, varkw, locals, inspect.formatargvalues(args, varargs, varkw, locals,
formatvalue=lambda value: '=' + pydoc.text.repr(value)) formatvalue=lambda value: '=' + pydoc.text.repr(value))
highlight = {} highlight = {}
def reader(lnum=[lnum]): def reader(lnum=[lnum]):
highlight[lnum[0]] = 1 highlight[lnum[0]] = 1
try: return linecache.getline(file, lnum[0]) try:
finally: lnum[0] += 1 return linecache.getline(file, lnum[0])
finally:
lnum[0] += 1
vars = scanvars(reader, frame, locals) vars = scanvars(reader, frame, locals)
rows = [' %s %s' % (file, call)] rows = [' %s %s' % (file, call)]
@ -111,17 +124,21 @@ function calls leading up to the error, in the order they occurred.
i = lnum - index i = lnum - index
for line in lines: for line in lines:
num = '%5d ' % i num = '%5d ' % i
rows.append(num+line.rstrip()) rows.append(num + line.rstrip())
i += 1 i += 1
done, dump = {}, [] done, dump = {}, []
for name, where, value in vars: for name, where, value in vars:
if name in done: continue if name in done:
continue
done[name] = 1 done[name] = 1
if value is not __UNDEF__: if value is not __UNDEF__:
if where == 'global': name = 'global ' + name if where == 'global':
elif where == 'local': name = name name = 'global ' + name
else: name = where + name.split('.')[-1] elif where == 'local':
name = name
else:
name = where + name.split('.')[-1]
dump.append('%s = %s' % (name, pydoc.text.repr(value))) dump.append('%s = %s' % (name, pydoc.text.repr(value)))
else: else:
dump.append(name + ' undefined') dump.append(name + ' undefined')
@ -130,10 +147,10 @@ function calls leading up to the error, in the order they occurred.
frames.append('\n%s\n' % '\n'.join(rows)) frames.append('\n%s\n' % '\n'.join(rows))
exception = ['%s: %s' % (str(etype), str(evalue))] exception = ['%s: %s' % (str(etype), str(evalue))]
if type(evalue) is types.InstanceType: if isinstance(evalue, types.InstanceType):
for name in dir(evalue): for name in dir(evalue):
value = pydoc.text.repr(getattr(evalue, name)) value = pydoc.text.repr(getattr(evalue, name))
exception.append('\n%s%s = %s' % (" "*4, name, value)) exception.append('\n%s%s = %s' % (" " * 4, name, value))
import traceback import traceback
return head + ''.join(frames) + ''.join(exception) + ''' return head + ''.join(frames) + ''.join(exception) + '''
@ -144,11 +161,12 @@ the original traceback:
%s %s
''' % ''.join(traceback.format_exception(etype, evalue, etb)) ''' % ''.join(traceback.format_exception(etype, evalue, etb))
class Hook: class Hook:
"""A hook to replace sys.excepthook that shows tracebacks in HTML.""" """A hook to replace sys.excepthook that shows tracebacks in HTML."""
def __init__(self, display=1, logdir=None, context=5, file=None, def __init__(self, display=1, logdir=None, context=5, file=None,
agi=None): agi=None):
self.display = display # send tracebacks to browser if true self.display = display # send tracebacks to browser if true
self.logdir = logdir # log tracebacks to files if not None self.logdir = logdir # log tracebacks to files if not None
self.context = context # number of source code lines per frame self.context = context # number of source code lines per frame
@ -180,7 +198,8 @@ class Hook:
self.file.write('A problem occured in a python script\n') self.file.write('A problem occured in a python script\n')
if self.logdir is not None: if self.logdir is not None:
import os, tempfile import os
import tempfile
(fd, path) = tempfile.mkstemp(suffix='.txt', dir=self.logdir) (fd, path) = tempfile.mkstemp(suffix='.txt', dir=self.logdir)
try: try:
file = os.fdopen(fd, 'w') file = os.fdopen(fd, 'w')
@ -197,20 +216,22 @@ class Hook:
try: try:
self.file.flush() self.file.flush()
except: pass except:
pass
handler = Hook().handle handler = Hook().handle
def enable(agi=None, display=1, logdir=None, context=5): def enable(agi=None, display=1, logdir=None, context=5):
"""Install an exception handler that formats tracebacks as HTML. """Install an exception handler that formats tracebacks as HTML.
The optional argument 'display' can be set to 0 to suppress sending the The optional argument 'display' can be set to 0 to suppress sending the
traceback to the browser, and 'logdir' can be set to a directory to cause traceback to the browser, and 'logdir' can be set to a directory to cause
tracebacks to be written to files there.""" tracebacks to be written to files there."""
except_hook = Hook(display=display, logdir=logdir, except_hook = Hook(display=display, logdir=logdir,
context=context, agi=agi) context=context, agi=agi)
sys.excepthook = except_hook sys.excepthook = except_hook
global handler global handler
handler = except_hook.handle handler = except_hook.handle

View File

@ -28,18 +28,22 @@ This module provides parsing functionality for asterisk config files.
import sys import sys
class ParseError(Exception): pass
class ParseError(Exception):
pass
class Line(object): class Line(object):
def __init__(self, line, number): def __init__(self, line, number):
self.line = '' self.line = ''
self.comment = '' self.comment = ''
line = line.strip() # I guess we don't preserve indentation line = line.strip() # I guess we don't preserve indentation
self.number = number self.number = number
parts = line.split(';') parts = line.split(';')
if len(parts) >= 2: if len(parts) >= 2:
self.line = parts[0].strip() self.line = parts[0].strip()
self.comment = ';'.join(parts[1:]) #Just in case the comment contained ';' self.comment = ';'.join(
parts[1:]) # Just in case the comment contained ';'
else: else:
self.line = line self.line = line
@ -59,12 +63,14 @@ class Category(Line):
Line.__init__(self, line, num) Line.__init__(self, line, num)
if self.line: if self.line:
if (self.line[0] != '[' or self.line[-1] != ']'): if (self.line[0] != '[' or self.line[-1] != ']'):
raise ParseError(self.number, "Missing '[' or ']' in category definition") raise ParseError(
self.number, "Missing '[' or ']' in category definition")
self.name = self.line[1:-1] self.name = self.line[1:-1]
elif name: elif name:
self.name = name self.name = name
else: else:
raise Exception("Must provide name or line representing a category") raise Exception(
"Must provide name or line representing a category")
self.items = [] self.items = []
self.comments = [] self.comments = []
@ -106,10 +112,11 @@ class Item(Line):
if self.line.strip()[-1] == ']': if self.line.strip()[-1] == ']':
raise ParseError(self.number, "Category name missing '['") raise ParseError(self.number, "Category name missing '['")
else: else:
raise ParseError(self.number, "Item must be in name = value pairs") raise ParseError(
self.number, "Item must be in name = value pairs")
if value and value[0] == '>': if value and value[0] == '>':
self.style = '>' #preserve the style of the original self.style = '>' # preserve the style of the original
value = value[1:].strip() value = value[1:].strip()
self.name = name.strip() self.name = name.strip()
self.value = value self.value = value
@ -119,6 +126,7 @@ class Item(Line):
return '%s =%s %s\t;%s' % (self.name, self.style, self.value, self.comment) return '%s =%s %s\t;%s' % (self.name, self.style, self.value, self.comment)
return '%s =%s %s' % (self.name, self.style, self.value) return '%s =%s %s' % (self.name, self.style, self.value)
class Config(object): class Config(object):
def __init__(self, filename): def __init__(self, filename):
self.filename = filename self.filename = filename
@ -127,8 +135,8 @@ class Config(object):
self.categories = [] self.categories = []
# load and parse the file # load and parse the file
self.load() self.load()
self.parse() self.parse()
def load(self): def load(self):
self.raw_lines = open(self.filename).readlines() self.raw_lines = open(self.filename).readlines()
@ -140,14 +148,15 @@ class Config(object):
def parse(self): def parse(self):
cat = None cat = None
num = 0 num = 0
for line in self.raw_lines: for line in self.raw_lines:
num += 1 num += 1
line = line.strip() line = line.strip()
if not line or line[0] == ';': if not line or line[0] == ';':
item = Line(line or '', num) item = Line(line or '', num)
self.lines.append(item) self.lines.append(item)
if cat: cat.comments.append(item) if cat:
cat.comments.append(item)
continue continue
elif line[0] == '[': elif line[0] == '[':
cat = Category(line, num) cat = Category(line, num)
@ -157,6 +166,6 @@ class Config(object):
else: else:
item = Item(line, num) item = Item(line, num)
self.lines.append(item) self.lines.append(item)
if cat: cat.append(item) if cat:
cat.append(item)
continue continue

View File

@ -52,7 +52,8 @@ Not all manager actions are implmented as of yet, feel free to add them
and submit patches. and submit patches.
""" """
import sys,os import sys
import os
import socket import socket
import threading import threading
import Queue import Queue
@ -63,6 +64,7 @@ from time import sleep
EOL = '\r\n' EOL = '\r\n'
class ManagerMsg(object): class ManagerMsg(object):
"""A manager interface message""" """A manager interface message"""
def __init__(self, response): def __init__(self, response):
@ -99,13 +101,13 @@ class ManagerMsg(object):
"""Parse a manager message""" """Parse a manager message"""
data = [] data = []
for n, line in enumerate (response): for n, line in enumerate(response):
# all valid header lines end in \r\n # all valid header lines end in \r\n
if not line.endswith ('\r\n'): if not line.endswith('\r\n'):
data.extend(response[n:]) data.extend(response[n:])
break break
try: try:
k, v = (x.strip() for x in line.split(':',1)) k, v = (x.strip() for x in line.split(':', 1))
self.headers[k] = v self.headers[k] = v
except ValueError: except ValueError:
# invalid header, start of multi-line data response # invalid header, start of multi-line data response
@ -115,9 +117,9 @@ class ManagerMsg(object):
def has_header(self, hname): def has_header(self, hname):
"""Check for a header""" """Check for a header"""
return self.headers.has_key(hname) return hname in self.headers
def get_header(self, hname, defval = None): def get_header(self, hname, defval=None):
"""Return the specfied header""" """Return the specfied header"""
return self.headers.get(hname, defval) return self.headers.get(hname, defval)
@ -143,16 +145,17 @@ class Event(object):
# if this is not an event message we have a problem # if this is not an event message we have a problem
if not message.has_header('Event'): if not message.has_header('Event'):
raise ManagerException('Trying to create event from non event message') raise ManagerException(
'Trying to create event from non event message')
# get the event name # get the event name
self.name = message.get_header('Event') self.name = message.get_header('Event')
def has_header(self, hname): def has_header(self, hname):
"""Check for a header""" """Check for a header"""
return self.headers.has_key(hname) return hname in self.headers
def get_header(self, hname, defval = None): def get_header(self, hname, defval=None):
"""Return the specfied header""" """Return the specfied header"""
return self.headers.get(hname, defval) return self.headers.get(hname, defval)
@ -164,7 +167,8 @@ class Event(object):
return self.headers['Event'] return self.headers['Event']
def get_action_id(self): def get_action_id(self):
return self.headers.get('ActionID',0000) return self.headers.get('ActionID', 0000)
class Manager(object): class Manager(object):
def __init__(self): def __init__(self):
@ -192,12 +196,12 @@ class Manager(object):
# some threads # some threads
self.message_thread = threading.Thread(target=self.message_loop) self.message_thread = threading.Thread(target=self.message_loop)
self.event_dispatch_thread = threading.Thread(target=self.event_dispatch) self.event_dispatch_thread = threading.Thread(
target=self.event_dispatch)
self.message_thread.setDaemon(True) self.message_thread.setDaemon(True)
self.event_dispatch_thread.setDaemon(True) self.event_dispatch_thread.setDaemon(True)
def __del__(self): def __del__(self):
self.close() self.close()
@ -241,18 +245,19 @@ class Manager(object):
cdict.update(kwargs) cdict.update(kwargs)
# set the action id # set the action id
if not cdict.has_key('ActionID'): cdict['ActionID'] = '%s-%08x' % (self.hostname, self.next_seq()) if 'ActionID' not in cdict:
cdict['ActionID'] = '%s-%08x' % (self.hostname, self.next_seq())
clist = [] clist = []
# generate the command # generate the command
for key, value in cdict.items(): for key, value in cdict.items():
if isinstance(value, list): if isinstance(value, list):
for item in value: for item in value:
item = tuple([key, item]) item = tuple([key, item])
clist.append('%s: %s' % item) clist.append('%s: %s' % item)
else: else:
item = tuple([key, value]) item = tuple([key, value])
clist.append('%s: %s' % item) clist.append('%s: %s' % item)
clist.append(EOL) clist.append(EOL)
command = EOL.join(clist) command = EOL.join(clist)
@ -263,7 +268,7 @@ class Manager(object):
except socket.error, (errno, reason): except socket.error, (errno, reason):
raise ManagerSocketException(errno, reason) raise ManagerSocketException(errno, reason)
self._reswaiting.insert(0,1) self._reswaiting.insert(0, 1)
response = self._response_queue.get() response = self._response_queue.get()
self._reswaiting.pop(0) self._reswaiting.pop(0)
@ -285,7 +290,7 @@ class Manager(object):
while self._running.isSet() and self._connected.isSet(): while self._running.isSet() and self._connected.isSet():
try: try:
lines = [] lines = []
for line in self._sock : for line in self._sock:
# check to see if this is the greeting line # check to see if this is the greeting line
if not self.title and '/' in line and not ':' in line: if not self.title and '/' in line and not ':' in line:
# store the title of the manager we are connecting to: # store the title of the manager we are connecting to:
@ -293,8 +298,8 @@ class Manager(object):
# store the version of the manager we are connecting to: # store the version of the manager we are connecting to:
self.version = line.split('/')[1].strip() self.version = line.split('/')[1].strip()
# fake message header # fake message header
lines.append ('Response: Generated Header\r\n') lines.append('Response: Generated Header\r\n')
lines.append (line) lines.append(line)
break break
# If the line is EOL marker we have a complete message. # If the line is EOL marker we have a complete message.
# Some commands are broken and contain a \n\r\n # Some commands are broken and contain a \n\r\n
@ -302,7 +307,7 @@ class Manager(object):
# have such a command where the data ends with the # have such a command where the data ends with the
# marker --END COMMAND--, so we ignore embedded # marker --END COMMAND--, so we ignore embedded
# newlines until we see that marker # newlines until we see that marker
if line == EOL and not wait_for_marker : if line == EOL and not wait_for_marker:
multiline = False multiline = False
if lines or not self._connected.isSet(): if lines or not self._connected.isSet():
break break
@ -321,7 +326,7 @@ class Manager(object):
# Response: Follows indicates we should wait for end # Response: Follows indicates we should wait for end
# marker --END COMMAND-- # marker --END COMMAND--
if not (multiline or status) and line.startswith('Response') and \ if not (multiline or status) and line.startswith('Response') and \
line.split(':', 1)[1].strip() == 'Follows': line.split(':', 1)[1].strip() == 'Follows':
wait_for_marker = True wait_for_marker = True
# same when seeing end of multiline response # same when seeing end of multiline response
if multiline and line.startswith('--END COMMAND--'): if multiline and line.startswith('--END COMMAND--'):
@ -347,7 +352,6 @@ class Manager(object):
self._connected.clear() self._connected.clear()
self._message_queue.put(None) self._message_queue.put(None)
def register_event(self, event, function): def register_event(self, event, function):
""" """
Register a callback for the specfied event. Register a callback for the specfied event.
@ -410,7 +414,6 @@ class Manager(object):
# wait for our data receiving thread to exit # wait for our data receiving thread to exit
t.join() t.join()
def event_dispatch(self): def event_dispatch(self):
"""This thread is responsible for dispatching events""" """This thread is responsible for dispatching events"""
@ -427,12 +430,12 @@ class Manager(object):
# first build a list of the functions to execute # first build a list of the functions to execute
callbacks = (self._event_callbacks.get(ev.name, []) callbacks = (self._event_callbacks.get(ev.name, [])
+ self._event_callbacks.get('*', [])) + self._event_callbacks.get('*', []))
# now execute the functions # now execute the functions
for callback in callbacks: for callback in callbacks:
if callback(ev, self): if callback(ev, self):
break break
def connect(self, host, port=5038): def connect(self, host, port=5038):
"""Connect to the manager interface""" """Connect to the manager interface"""
@ -448,9 +451,9 @@ class Manager(object):
# create our socket and connect # create our socket and connect
try: try:
_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) _sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
_sock.connect((host,port)) _sock.connect((host, port))
self._sock = _sock.makefile () self._sock = _sock.makefile()
_sock.close () _sock.close()
except socket.error, (errno, reason): except socket.error, (errno, reason):
raise ManagerSocketException(errno, reason) raise ManagerSocketException(errno, reason)
@ -493,26 +496,26 @@ class Manager(object):
def login(self, username, secret): def login(self, username, secret):
"""Login to the manager, throws ManagerAuthException when login falis""" """Login to the manager, throws ManagerAuthException when login falis"""
cdict = {'Action':'Login'} cdict = {'Action': 'Login'}
cdict['Username'] = username cdict['Username'] = username
cdict['Secret'] = secret cdict['Secret'] = secret
response = self.send_action(cdict) response = self.send_action(cdict)
if response.get_header('Response') == 'Error': if response.get_header('Response') == 'Error':
raise ManagerAuthException(response.get_header('Message')) raise ManagerAuthException(response.get_header('Message'))
return response return response
def ping(self): def ping(self):
"""Send a ping action to the manager""" """Send a ping action to the manager"""
cdict = {'Action':'Ping'} cdict = {'Action': 'Ping'}
response = self.send_action(cdict) response = self.send_action(cdict)
return response return response
def logoff(self): def logoff(self):
"""Logoff from the manager""" """Logoff from the manager"""
cdict = {'Action':'Logoff'} cdict = {'Action': 'Logoff'}
response = self.send_action(cdict) response = self.send_action(cdict)
return response return response
@ -520,16 +523,16 @@ class Manager(object):
def hangup(self, channel): def hangup(self, channel):
"""Hangup the specified channel""" """Hangup the specified channel"""
cdict = {'Action':'Hangup'} cdict = {'Action': 'Hangup'}
cdict['Channel'] = channel cdict['Channel'] = channel
response = self.send_action(cdict) response = self.send_action(cdict)
return response return response
def status(self, channel = ''): def status(self, channel=''):
"""Get a status message from asterisk""" """Get a status message from asterisk"""
cdict = {'Action':'Status'} cdict = {'Action': 'Status'}
cdict['Channel'] = channel cdict['Channel'] = channel
response = self.send_action(cdict) response = self.send_action(cdict)
@ -538,12 +541,14 @@ class Manager(object):
def redirect(self, channel, exten, priority='1', extra_channel='', context=''): def redirect(self, channel, exten, priority='1', extra_channel='', context=''):
"""Redirect a channel""" """Redirect a channel"""
cdict = {'Action':'Redirect'} cdict = {'Action': 'Redirect'}
cdict['Channel'] = channel cdict['Channel'] = channel
cdict['Exten'] = exten cdict['Exten'] = exten
cdict['Priority'] = priority cdict['Priority'] = priority
if context: cdict['Context'] = context if context:
if extra_channel: cdict['ExtraChannel'] = extra_channel cdict['Context'] = context
if extra_channel:
cdict['ExtraChannel'] = extra_channel
response = self.send_action(cdict) response = self.send_action(cdict)
return response return response
@ -551,19 +556,27 @@ class Manager(object):
def originate(self, channel, exten, context='', priority='', timeout='', caller_id='', async=False, account='', variables={}): def originate(self, channel, exten, context='', priority='', timeout='', caller_id='', async=False, account='', variables={}):
"""Originate a call""" """Originate a call"""
cdict = {'Action':'Originate'} cdict = {'Action': 'Originate'}
cdict['Channel'] = channel cdict['Channel'] = channel
cdict['Exten'] = exten cdict['Exten'] = exten
if context: cdict['Context'] = context if context:
if priority: cdict['Priority'] = priority cdict['Context'] = context
if timeout: cdict['Timeout'] = timeout if priority:
if caller_id: cdict['CallerID'] = caller_id cdict['Priority'] = priority
if async: cdict['Async'] = 'yes' if timeout:
if account: cdict['Account'] = account cdict['Timeout'] = timeout
if caller_id:
cdict['CallerID'] = caller_id
if async:
cdict['Async'] = 'yes'
if account:
cdict['Account'] = account
# join dict of vairables together in a string in the form of 'key=val|key=val' # join dict of vairables together in a string in the form of 'key=val|key=val'
# with the latest CVS HEAD this is no longer necessary # with the latest CVS HEAD this is no longer necessary
# if variables: cdict['Variable'] = '|'.join(['='.join((str(key), str(value))) for key, value in variables.items()]) # if variables: cdict['Variable'] = '|'.join(['='.join((str(key), str(value))) for key, value in variables.items()])
if variables: cdict['Variable'] = ['='.join((str(key), str(value))) for key, value in variables.items()] if variables:
cdict['Variable'] = ['='.join(
(str(key), str(value))) for key, value in variables.items()]
response = self.send_action(cdict) response = self.send_action(cdict)
@ -572,7 +585,7 @@ class Manager(object):
def mailbox_status(self, mailbox): def mailbox_status(self, mailbox):
"""Get the status of the specfied mailbox""" """Get the status of the specfied mailbox"""
cdict = {'Action':'MailboxStatus'} cdict = {'Action': 'MailboxStatus'}
cdict['Mailbox'] = mailbox cdict['Mailbox'] = mailbox
response = self.send_action(cdict) response = self.send_action(cdict)
@ -581,7 +594,7 @@ class Manager(object):
def command(self, command): def command(self, command):
"""Execute a command""" """Execute a command"""
cdict = {'Action':'Command'} cdict = {'Action': 'Command'}
cdict['Command'] = command cdict['Command'] = command
response = self.send_action(cdict) response = self.send_action(cdict)
@ -590,16 +603,16 @@ class Manager(object):
def extension_state(self, exten, context): def extension_state(self, exten, context):
"""Get the state of an extension""" """Get the state of an extension"""
cdict = {'Action':'ExtensionState'} cdict = {'Action': 'ExtensionState'}
cdict['Exten'] = exten cdict['Exten'] = exten
cdict['Context'] = context cdict['Context'] = context
response = self.send_action(cdict) response = self.send_action(cdict)
return response return response
def playdtmf (self, channel, digit) : def playdtmf(self, channel, digit):
"""Plays a dtmf digit on the specified channel""" """Plays a dtmf digit on the specified channel"""
cdict = {'Action':'PlayDTMF'} cdict = {'Action': 'PlayDTMF'}
cdict['Channel'] = channel cdict['Channel'] = channel
cdict['Digit'] = digit cdict['Digit'] = digit
response = self.send_action(cdict) response = self.send_action(cdict)
@ -609,7 +622,7 @@ class Manager(object):
def absolute_timeout(self, channel, timeout): def absolute_timeout(self, channel, timeout):
"""Set an absolute timeout on a channel""" """Set an absolute timeout on a channel"""
cdict = {'Action':'AbsoluteTimeout'} cdict = {'Action': 'AbsoluteTimeout'}
cdict['Channel'] = channel cdict['Channel'] = channel
cdict['Timeout'] = timeout cdict['Timeout'] = timeout
response = self.send_action(cdict) response = self.send_action(cdict)
@ -617,25 +630,31 @@ class Manager(object):
return response return response
def mailbox_count(self, mailbox): def mailbox_count(self, mailbox):
cdict = {'Action':'MailboxCount'} cdict = {'Action': 'MailboxCount'}
cdict['Mailbox'] = mailbox cdict['Mailbox'] = mailbox
response = self.send_action(cdict) response = self.send_action(cdict)
return response return response
def sippeers(self): def sippeers(self):
cdict = {'Action' : 'Sippeers'} cdict = {'Action': 'Sippeers'}
response = self.send_action(cdict) response = self.send_action(cdict)
return response return response
def sipshowpeer(self, peer): def sipshowpeer(self, peer):
cdict = {'Action' : 'SIPshowpeer'} cdict = {'Action': 'SIPshowpeer'}
cdict['Peer'] = peer cdict['Peer'] = peer
response = self.send_action(cdict) response = self.send_action(cdict)
return response return response
class ManagerException(Exception): pass class ManagerException(Exception):
class ManagerSocketException(ManagerException): pass pass
class ManagerAuthException(ManagerException): pass
class ManagerSocketException(ManagerException):
pass
class ManagerAuthException(ManagerException):
pass

View File

@ -6,45 +6,43 @@ from asterisk import __version__ as version
description = [] description = []
f = open ('README') f = open('README')
logo_stripped = False logo_stripped = False
for line in f : for line in f:
if not logo_stripped and line.strip () : if not logo_stripped and line.strip():
continue continue
logo_stripped = True logo_stripped = True
description.append (line) description.append(line)
licenses = ( 'Python Software Foundation License' licenses = ('Python Software Foundation License',
, 'GNU Library or Lesser General Public License (LGPL)' 'GNU Library or Lesser General Public License (LGPL)')
)
setup(
setup \ name='pyst2',
( name = 'pyst2' version=version,
, version = version description='A Python Interface to Asterisk',
, description = 'A Python Interface to Asterisk' long_description=''.join(description), author='Karl Putland',
, long_description = ''.join (description) author_email='kputland@users.sourceforge.net',
, author = 'Karl Putland' maintainer='Randall Degges',
, author_email = 'kputland@users.sourceforge.net' maintainer_email='rdegges@gmail.com',
, maintainer = 'Randall Degges' url='http://www.sourceforge.net/projects/pyst/',
, maintainer_email = 'rdegges@gmail.com' packages=['asterisk'],
, url = 'http://www.sourceforge.net/projects/pyst/' license=', '.join(licenses),
, packages = ['asterisk'] platforms='Any',
, license = ', '.join (licenses) classifiers=[
, platforms = 'Any' 'Development Status :: 5 - Production/Stable',
, classifiers = 'Environment :: Other Environment',
[ 'Development Status :: 5 - Production/Stable' 'Intended Audience :: Developers',
, 'Environment :: Other Environment' 'Intended Audience :: Telecommunications Industry',
, 'Intended Audience :: Developers' 'Operating System :: OS Independent',
, 'Intended Audience :: Telecommunications Industry' 'Programming Language :: Python',
, 'Operating System :: OS Independent' 'Programming Language :: Python :: 2.4',
, 'Programming Language :: Python' 'Programming Language :: Python :: 2.5',
, 'Programming Language :: Python :: 2.4' 'Programming Language :: Python :: 2.6',
, 'Programming Language :: Python :: 2.5' 'Programming Language :: Python :: 2.7',
, 'Programming Language :: Python :: 2.6' 'Topic :: Communications :: Internet Phone',
, 'Programming Language :: Python :: 2.7' 'Topic :: Communications :: Telephony',
, 'Topic :: Communications :: Internet Phone' 'Topic :: Software Development :: Libraries :: Python Modules'
, 'Topic :: Communications :: Telephony' ] + ['License :: OSI Approved :: ' + l for l in licenses]
, 'Topic :: Software Development :: Libraries :: Python Modules' )
] + ['License :: OSI Approved :: ' + l for l in licenses]
)