commit
2664927f02
|
@ -1,4 +1,8 @@
|
|||
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
|
||||
proper output.
|
||||
|
||||
|
@ -94,4 +98,3 @@
|
|||
* ChangeLog: Added.
|
||||
* TODO: Added.
|
||||
* MANIFEST.in: Added ChangeLog and TODO.
|
||||
|
||||
|
|
102
asterisk/agi.py
102
asterisk/agi.py
|
@ -19,7 +19,9 @@ pyvr
|
|||
|
||||
"""
|
||||
|
||||
import sys, pprint, re
|
||||
import sys
|
||||
import pprint
|
||||
import re
|
||||
from types import ListType
|
||||
import signal
|
||||
|
||||
|
@ -29,24 +31,53 @@ DEFAULT_RECORD = 20000 # 20sec record time
|
|||
re_code = re.compile(r'(^\d*)\s*(.*)')
|
||||
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
|
||||
# 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 AGIInvalidCommand(AGIError): pass
|
||||
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 AGIInvalidCommand(AGIError):
|
||||
pass
|
||||
|
||||
|
||||
class AGI:
|
||||
"""
|
||||
|
@ -76,7 +107,7 @@ class AGI:
|
|||
key, data = line.split(':')[0], ':'.join(line.split(':')[1:])
|
||||
key = key.strip()
|
||||
data = data.strip()
|
||||
if key <> '':
|
||||
if key != '':
|
||||
self.env[key] = data
|
||||
sys.stderr.write('class AGI: self.env = ')
|
||||
sys.stderr.write(pprint.pformat(self.env))
|
||||
|
@ -226,7 +257,8 @@ class AGI:
|
|||
extension must not be included in the filename.
|
||||
"""
|
||||
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]
|
||||
if res == '0':
|
||||
return ''
|
||||
|
@ -265,7 +297,8 @@ class AGI:
|
|||
"""
|
||||
res = self.execute('SEND IMAGE', filename)['result'][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=''):
|
||||
"""agi.say_digits(digits, escape_digits='') --> digit
|
||||
|
@ -326,7 +359,8 @@ class AGI:
|
|||
"""
|
||||
characters = self._process_digit_list(characters)
|
||||
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':
|
||||
return ''
|
||||
else:
|
||||
|
@ -372,8 +406,10 @@ class AGI:
|
|||
in seconds since the UNIX Epoch (Jan 1, 1970 00:00:00).
|
||||
"""
|
||||
escape_digits = self._process_digit_list(escape_digits)
|
||||
if format: format = self._quote(format)
|
||||
res = self.execute('SAY DATETIME', seconds, escape_digits, format, zone)['result'][0]
|
||||
if format:
|
||||
format = self._quote(format)
|
||||
res = self.execute(
|
||||
'SAY DATETIME', seconds, escape_digits, format, zone)['result'][0]
|
||||
if res == '0':
|
||||
return ''
|
||||
else:
|
||||
|
@ -401,7 +437,8 @@ class AGI:
|
|||
"""
|
||||
escape_digits = self._process_digit_list(escape_digits)
|
||||
if timeout:
|
||||
response = self.execute('GET OPTION', filename, escape_digits, timeout)
|
||||
response = self.execute(
|
||||
'GET OPTION', filename, escape_digits, timeout)
|
||||
else:
|
||||
response = self.execute('GET OPTION', filename, escape_digits)
|
||||
|
||||
|
@ -455,7 +492,8 @@ class AGI:
|
|||
exceeding the end of the file
|
||||
"""
|
||||
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:
|
||||
return chr(int(res))
|
||||
except:
|
||||
|
@ -545,7 +583,8 @@ class AGI:
|
|||
"""
|
||||
try:
|
||||
if channel:
|
||||
result = self.execute('GET FULL VARIABLE', self._quote(name), self._quote(channel))
|
||||
result = self.execute('GET FULL VARIABLE',
|
||||
self._quote(name), self._quote(channel))
|
||||
else:
|
||||
result = self.execute('GET FULL VARIABLE', self._quote(name))
|
||||
|
||||
|
@ -571,10 +610,12 @@ class AGI:
|
|||
"""
|
||||
family = '"%s"' % family
|
||||
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']
|
||||
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':
|
||||
return value
|
||||
else:
|
||||
|
@ -585,7 +626,8 @@ class AGI:
|
|||
Adds or updates an entry in the Asterisk database for a
|
||||
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']
|
||||
if res == '0':
|
||||
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
|
||||
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']
|
||||
if res == '0':
|
||||
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
|
||||
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']
|
||||
if res == '0':
|
||||
raise AGIDBError('Unable to delete tree from database: family=%s, key=%s' % (family, key))
|
||||
|
@ -640,7 +684,8 @@ if __name__=='__main__':
|
|||
try:
|
||||
agi.appexec('backgrounder', 'demo-congrats')
|
||||
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.get_variable('foo')
|
||||
|
@ -661,6 +706,7 @@ if __name__=='__main__':
|
|||
agi.database_del('foo', 'bar')
|
||||
agi.database_deltree('foo')
|
||||
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()
|
||||
|
|
|
@ -36,6 +36,7 @@ import sys
|
|||
|
||||
__UNDEF__ = [] # a special sentinel object
|
||||
|
||||
|
||||
def lookup(name, frame, locals):
|
||||
"""Find the value for a given name in the given environment."""
|
||||
if name in locals:
|
||||
|
@ -44,7 +45,7 @@ def lookup(name, frame, locals):
|
|||
return 'global', frame.f_globals[name]
|
||||
if '__builtins__' in frame.f_globals:
|
||||
builtins = frame.f_globals['__builtins__']
|
||||
if type(builtins) is type({}):
|
||||
if isinstance(builtins, type({})):
|
||||
if name in builtins:
|
||||
return 'builtin', builtins[name]
|
||||
else:
|
||||
|
@ -52,12 +53,15 @@ def lookup(name, frame, locals):
|
|||
return 'builtin', getattr(builtins, name)
|
||||
return None, __UNDEF__
|
||||
|
||||
|
||||
def scanvars(reader, frame, locals):
|
||||
"""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__
|
||||
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 lasttoken == '.':
|
||||
if parent is not __UNDEF__:
|
||||
|
@ -77,9 +81,15 @@ def scanvars(reader, frame, locals):
|
|||
|
||||
def text((etype, evalue, etb), context=5):
|
||||
"""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__
|
||||
pyver = 'Python ' + sys.version.split()[0] + ': ' + sys.executable
|
||||
date = time.ctime(time.time())
|
||||
|
@ -100,10 +110,13 @@ function calls leading up to the error, in the order they occurred.
|
|||
formatvalue=lambda value: '=' + pydoc.text.repr(value))
|
||||
|
||||
highlight = {}
|
||||
|
||||
def reader(lnum=[lnum]):
|
||||
highlight[lnum[0]] = 1
|
||||
try: return linecache.getline(file, lnum[0])
|
||||
finally: lnum[0] += 1
|
||||
try:
|
||||
return linecache.getline(file, lnum[0])
|
||||
finally:
|
||||
lnum[0] += 1
|
||||
vars = scanvars(reader, frame, locals)
|
||||
|
||||
rows = [' %s %s' % (file, call)]
|
||||
|
@ -116,12 +129,16 @@ function calls leading up to the error, in the order they occurred.
|
|||
|
||||
done, dump = {}, []
|
||||
for name, where, value in vars:
|
||||
if name in done: continue
|
||||
if name in done:
|
||||
continue
|
||||
done[name] = 1
|
||||
if value is not __UNDEF__:
|
||||
if where == 'global': name = 'global ' + name
|
||||
elif where == 'local': name = name
|
||||
else: name = where + name.split('.')[-1]
|
||||
if where == 'global':
|
||||
name = 'global ' + name
|
||||
elif where == 'local':
|
||||
name = name
|
||||
else:
|
||||
name = where + name.split('.')[-1]
|
||||
dump.append('%s = %s' % (name, pydoc.text.repr(value)))
|
||||
else:
|
||||
dump.append(name + ' undefined')
|
||||
|
@ -130,7 +147,7 @@ function calls leading up to the error, in the order they occurred.
|
|||
frames.append('\n%s\n' % '\n'.join(rows))
|
||||
|
||||
exception = ['%s: %s' % (str(etype), str(evalue))]
|
||||
if type(evalue) is types.InstanceType:
|
||||
if isinstance(evalue, types.InstanceType):
|
||||
for name in dir(evalue):
|
||||
value = pydoc.text.repr(getattr(evalue, name))
|
||||
exception.append('\n%s%s = %s' % (" " * 4, name, value))
|
||||
|
@ -144,6 +161,7 @@ the original traceback:
|
|||
%s
|
||||
''' % ''.join(traceback.format_exception(etype, evalue, etb))
|
||||
|
||||
|
||||
class Hook:
|
||||
"""A hook to replace sys.excepthook that shows tracebacks in HTML."""
|
||||
|
||||
|
@ -180,7 +198,8 @@ class Hook:
|
|||
self.file.write('A problem occured in a python script\n')
|
||||
|
||||
if self.logdir is not None:
|
||||
import os, tempfile
|
||||
import os
|
||||
import tempfile
|
||||
(fd, path) = tempfile.mkstemp(suffix='.txt', dir=self.logdir)
|
||||
try:
|
||||
file = os.fdopen(fd, 'w')
|
||||
|
@ -197,10 +216,13 @@ class Hook:
|
|||
|
||||
try:
|
||||
self.file.flush()
|
||||
except: pass
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
handler = Hook().handle
|
||||
|
||||
|
||||
def enable(agi=None, display=1, logdir=None, context=5):
|
||||
"""Install an exception handler that formats tracebacks as HTML.
|
||||
|
||||
|
@ -213,4 +235,3 @@ def enable(agi=None, display=1, logdir=None, context=5):
|
|||
|
||||
global handler
|
||||
handler = except_hook.handle
|
||||
|
||||
|
|
|
@ -28,7 +28,10 @@ This module provides parsing functionality for asterisk config files.
|
|||
|
||||
import sys
|
||||
|
||||
class ParseError(Exception): pass
|
||||
|
||||
class ParseError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class Line(object):
|
||||
def __init__(self, line, number):
|
||||
|
@ -39,7 +42,8 @@ class Line(object):
|
|||
parts = line.split(';')
|
||||
if len(parts) >= 2:
|
||||
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:
|
||||
self.line = line
|
||||
|
||||
|
@ -59,12 +63,14 @@ class Category(Line):
|
|||
Line.__init__(self, line, num)
|
||||
if self.line:
|
||||
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]
|
||||
elif name:
|
||||
self.name = name
|
||||
else:
|
||||
raise Exception("Must provide name or line representing a category")
|
||||
raise Exception(
|
||||
"Must provide name or line representing a category")
|
||||
|
||||
self.items = []
|
||||
self.comments = []
|
||||
|
@ -106,7 +112,8 @@ class Item(Line):
|
|||
if self.line.strip()[-1] == ']':
|
||||
raise ParseError(self.number, "Category name missing '['")
|
||||
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] == '>':
|
||||
self.style = '>' # preserve the style of the original
|
||||
|
@ -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' % (self.name, self.style, self.value)
|
||||
|
||||
|
||||
class Config(object):
|
||||
def __init__(self, filename):
|
||||
self.filename = filename
|
||||
|
@ -147,7 +155,8 @@ class Config(object):
|
|||
if not line or line[0] == ';':
|
||||
item = Line(line or '', num)
|
||||
self.lines.append(item)
|
||||
if cat: cat.comments.append(item)
|
||||
if cat:
|
||||
cat.comments.append(item)
|
||||
continue
|
||||
elif line[0] == '[':
|
||||
cat = Category(line, num)
|
||||
|
@ -157,6 +166,6 @@ class Config(object):
|
|||
else:
|
||||
item = Item(line, num)
|
||||
self.lines.append(item)
|
||||
if cat: cat.append(item)
|
||||
if cat:
|
||||
cat.append(item)
|
||||
continue
|
||||
|
||||
|
|
|
@ -52,7 +52,8 @@ Not all manager actions are implmented as of yet, feel free to add them
|
|||
and submit patches.
|
||||
"""
|
||||
|
||||
import sys,os
|
||||
import sys
|
||||
import os
|
||||
import socket
|
||||
import threading
|
||||
import Queue
|
||||
|
@ -63,6 +64,7 @@ from time import sleep
|
|||
|
||||
EOL = '\r\n'
|
||||
|
||||
|
||||
class ManagerMsg(object):
|
||||
"""A manager interface message"""
|
||||
def __init__(self, response):
|
||||
|
@ -115,7 +117,7 @@ class ManagerMsg(object):
|
|||
|
||||
def has_header(self, hname):
|
||||
"""Check for a header"""
|
||||
return self.headers.has_key(hname)
|
||||
return hname in self.headers
|
||||
|
||||
def get_header(self, hname, defval=None):
|
||||
"""Return the specfied header"""
|
||||
|
@ -143,14 +145,15 @@ class Event(object):
|
|||
|
||||
# if this is not an event message we have a problem
|
||||
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
|
||||
self.name = message.get_header('Event')
|
||||
|
||||
def has_header(self, hname):
|
||||
"""Check for a header"""
|
||||
return self.headers.has_key(hname)
|
||||
return hname in self.headers
|
||||
|
||||
def get_header(self, hname, defval=None):
|
||||
"""Return the specfied header"""
|
||||
|
@ -166,6 +169,7 @@ class Event(object):
|
|||
def get_action_id(self):
|
||||
return self.headers.get('ActionID', 0000)
|
||||
|
||||
|
||||
class Manager(object):
|
||||
def __init__(self):
|
||||
self._sock = None # our socket
|
||||
|
@ -192,12 +196,12 @@ class Manager(object):
|
|||
|
||||
# some threads
|
||||
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.event_dispatch_thread.setDaemon(True)
|
||||
|
||||
|
||||
def __del__(self):
|
||||
self.close()
|
||||
|
||||
|
@ -241,7 +245,8 @@ class Manager(object):
|
|||
cdict.update(kwargs)
|
||||
|
||||
# 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 = []
|
||||
|
||||
# generate the command
|
||||
|
@ -347,7 +352,6 @@ class Manager(object):
|
|||
self._connected.clear()
|
||||
self._message_queue.put(None)
|
||||
|
||||
|
||||
def register_event(self, event, function):
|
||||
"""
|
||||
Register a callback for the specfied event.
|
||||
|
@ -410,7 +414,6 @@ class Manager(object):
|
|||
# wait for our data receiving thread to exit
|
||||
t.join()
|
||||
|
||||
|
||||
def event_dispatch(self):
|
||||
"""This thread is responsible for dispatching events"""
|
||||
|
||||
|
@ -542,8 +545,10 @@ class Manager(object):
|
|||
cdict['Channel'] = channel
|
||||
cdict['Exten'] = exten
|
||||
cdict['Priority'] = priority
|
||||
if context: cdict['Context'] = context
|
||||
if extra_channel: cdict['ExtraChannel'] = extra_channel
|
||||
if context:
|
||||
cdict['Context'] = context
|
||||
if extra_channel:
|
||||
cdict['ExtraChannel'] = extra_channel
|
||||
response = self.send_action(cdict)
|
||||
|
||||
return response
|
||||
|
@ -554,16 +559,24 @@ class Manager(object):
|
|||
cdict = {'Action': 'Originate'}
|
||||
cdict['Channel'] = channel
|
||||
cdict['Exten'] = exten
|
||||
if context: cdict['Context'] = context
|
||||
if priority: cdict['Priority'] = priority
|
||||
if timeout: cdict['Timeout'] = timeout
|
||||
if caller_id: cdict['CallerID'] = caller_id
|
||||
if async: cdict['Async'] = 'yes'
|
||||
if account: cdict['Account'] = account
|
||||
if context:
|
||||
cdict['Context'] = context
|
||||
if priority:
|
||||
cdict['Priority'] = priority
|
||||
if timeout:
|
||||
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'
|
||||
# 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((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)
|
||||
|
||||
|
@ -635,7 +648,13 @@ class Manager(object):
|
|||
return response
|
||||
|
||||
|
||||
class ManagerException(Exception): pass
|
||||
class ManagerSocketException(ManagerException): pass
|
||||
class ManagerAuthException(ManagerException): pass
|
||||
class ManagerException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class ManagerSocketException(ManagerException):
|
||||
pass
|
||||
|
||||
|
||||
class ManagerAuthException(ManagerException):
|
||||
pass
|
||||
|
|
60
setup.py
60
setup.py
|
@ -7,6 +7,7 @@ from asterisk import __version__ as version
|
|||
|
||||
description = []
|
||||
f = open('README')
|
||||
|
||||
logo_stripped = False
|
||||
for line in f:
|
||||
if not logo_stripped and line.strip():
|
||||
|
@ -14,37 +15,34 @@ for line in f :
|
|||
logo_stripped = True
|
||||
description.append(line)
|
||||
|
||||
licenses = ( 'Python Software Foundation License'
|
||||
, 'GNU Library or Lesser General Public License (LGPL)'
|
||||
)
|
||||
licenses = ('Python Software Foundation License',
|
||||
'GNU Library or Lesser General Public License (LGPL)')
|
||||
|
||||
|
||||
setup \
|
||||
( name = 'pyst2'
|
||||
, version = version
|
||||
, description = 'A Python Interface to Asterisk'
|
||||
, long_description = ''.join (description)
|
||||
, author = 'Karl Putland'
|
||||
, author_email = 'kputland@users.sourceforge.net'
|
||||
, maintainer = 'Randall Degges'
|
||||
, maintainer_email = 'rdegges@gmail.com'
|
||||
, url = 'http://www.sourceforge.net/projects/pyst/'
|
||||
, packages = ['asterisk']
|
||||
, license = ', '.join (licenses)
|
||||
, platforms = 'Any'
|
||||
, classifiers =
|
||||
[ 'Development Status :: 5 - Production/Stable'
|
||||
, 'Environment :: Other Environment'
|
||||
, 'Intended Audience :: Developers'
|
||||
, 'Intended Audience :: Telecommunications Industry'
|
||||
, 'Operating System :: OS Independent'
|
||||
, 'Programming Language :: Python'
|
||||
, 'Programming Language :: Python :: 2.4'
|
||||
, 'Programming Language :: Python :: 2.5'
|
||||
, 'Programming Language :: Python :: 2.6'
|
||||
, 'Programming Language :: Python :: 2.7'
|
||||
, 'Topic :: Communications :: Internet Phone'
|
||||
, 'Topic :: Communications :: Telephony'
|
||||
, 'Topic :: Software Development :: Libraries :: Python Modules'
|
||||
setup(
|
||||
name='pyst2',
|
||||
version=version,
|
||||
description='A Python Interface to Asterisk',
|
||||
long_description=''.join(description), author='Karl Putland',
|
||||
author_email='kputland@users.sourceforge.net',
|
||||
maintainer='Randall Degges',
|
||||
maintainer_email='rdegges@gmail.com',
|
||||
url='http://www.sourceforge.net/projects/pyst/',
|
||||
packages=['asterisk'],
|
||||
license=', '.join(licenses),
|
||||
platforms='Any',
|
||||
classifiers=[
|
||||
'Development Status :: 5 - Production/Stable',
|
||||
'Environment :: Other Environment',
|
||||
'Intended Audience :: Developers',
|
||||
'Intended Audience :: Telecommunications Industry',
|
||||
'Operating System :: OS Independent',
|
||||
'Programming Language :: Python',
|
||||
'Programming Language :: Python :: 2.4',
|
||||
'Programming Language :: Python :: 2.5',
|
||||
'Programming Language :: Python :: 2.6',
|
||||
'Programming Language :: Python :: 2.7',
|
||||
'Topic :: Communications :: Internet Phone',
|
||||
'Topic :: Communications :: Telephony',
|
||||
'Topic :: Software Development :: Libraries :: Python Modules'
|
||||
] + ['License :: OSI Approved :: ' + l for l in licenses]
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue