<p>George Joseph <strong>merged</strong> this change.</p><p><a href="https://gerrit.asterisk.org/8990">View Change</a></p><div style="white-space:pre-wrap">Approvals:
  Kevin Harwell: Looks good to me, but someone else must approve
  George Joseph: Looks good to me, approved; Approved for Submit

</div><pre style="font-family: monospace,monospace; white-space: pre-wrap;">Fix pep8 and flake8 issues.<br><br>* Update examples to be compatible with python3.<br>* Address pep8 and flake8 issues doc/, starpy/ and examples/.<br>* Fix undefined name keytree in fastagi:FastAGIProtocol.databaseDeltree.<br>* Set maximum line length to 90.<br><br>Change-Id: I293635427a48e66298042c3fc1e600e47854d3ed<br>---<br>M doc/pydoc/builddocs.py<br>M doc/pydoc/pydoc2.py<br>M examples/amicommand.py<br>M examples/autosurvey/frontend.py<br>M examples/calldurationcallback.py<br>M examples/connecttoivr.py<br>M examples/connecttoivrapp.py<br>M examples/fastagisetvariable.py<br>M examples/getvariable.py<br>M examples/hellofastagi.py<br>M examples/hellofastagiapp.py<br>M examples/menu.py<br>M examples/menutest.py<br>M examples/priexhaustion.py<br>M examples/priexhaustionbare.py<br>M examples/readingdigits.py<br>M examples/timestamp.py<br>M examples/timestampapp.py<br>M examples/utilapplication.py<br>M starpy/fastagi.py<br>M starpy/manager.py<br>M tox.ini<br>22 files changed, 1,496 insertions(+), 1,343 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">diff --git a/doc/pydoc/builddocs.py b/doc/pydoc/builddocs.py<br>index 080f7c6..9372eb1 100755<br>--- a/doc/pydoc/builddocs.py<br>+++ b/doc/pydoc/builddocs.py<br>@@ -2,26 +2,25 @@<br> import pydoc2<br> <br> if __name__ == "__main__":<br>-  excludes = [<br>-         "Numeric",<br>-         "_tkinter",<br>-                "Tkinter",<br>-         "math",<br>-            "string",<br>-          "twisted",<br>- ]<br>-    stops = [<br>-    ]<br>+    excludes = [<br>+        "Numeric",<br>+        "_tkinter",<br>+        "Tkinter",<br>+        "math",<br>+        "string",<br>+        "twisted",<br>+    ]<br>+    stops = [<br>+    ]<br> <br>-       modules = [<br>-          'starpy',<br>-            'starpy.examples',<br>-           '__builtin__',<br>-       ]       <br>-     pydoc2.PackageDocumentationGenerator(<br>-                baseModules = modules,<br>-               destinationDirectory = ".",<br>-                exclusions = excludes,<br>-               recursionStops = stops,<br>-      ).process ()<br>- <br>+    modules = [<br>+        'starpy',<br>+        'starpy.examples',<br>+        '__builtin__',<br>+    ]<br>+    pydoc2.PackageDocumentationGenerator(<br>+        baseModules=modules,<br>+        destinationDirectory=".",<br>+        exclusions=excludes,<br>+        recursionStops=stops,<br>+    ).process()<br>diff --git a/doc/pydoc/pydoc2.py b/doc/pydoc/pydoc2.py<br>index 8fc4d33..cc85c6a 100644<br>--- a/doc/pydoc/pydoc2.py<br>+++ b/doc/pydoc/pydoc2.py<br>@@ -1,463 +1,462 @@<br> """Pydoc sub-class for generating documentation for entire packages"""<br>-import pydoc, inspect, os, string<br>-import sys, imp, os, stat, re, types, inspect<br>-from repr import Repr<br>-from string import expandtabs, find, join, lower, split, strip, rfind, rstrip<br>+import pydoc<br>+import inspect<br>+import os<br>+import string<br>+import sys<br>+from string import join, split, strip<br>+<br> <br> def classify_class_attrs(cls):<br>-  """Return list of attribute-descriptor tuples.<br>+    """Return list of attribute-descriptor tuples.<br> <br>- For each name in dir(cls), the return list contains a 4-tuple<br>-        with these elements:<br>+    For each name in dir(cls), the return list contains a 4-tuple<br>+    with these elements:<br> <br>-               0. The name (a string).<br>+        0. The name (a string).<br> <br>-         1. The kind of attribute this is, one of these strings:<br>-                         'class method'    created via classmethod()<br>-                          'static method'   created via staticmethod()<br>-                         'property'        created via property()<br>-                     'method'          any other flavor of method<br>-                         'data'            not a method<br>+        1. The kind of attribute this is, one of these strings:<br>+               'class method'    created via classmethod()<br>+               'static method'   created via staticmethod()<br>+               'property'        created via property()<br>+               'method'          any other flavor of method<br>+               'data'            not a method<br> <br>-            2. The class which defined this attribute (a class).<br>+        2. The class which defined this attribute (a class).<br> <br>-               3. The object as obtained directly from the defining class's<br>-                __dict__, not via getattr.  This is especially important for<br>-                 data attributes:  C.data is just a data object, but<br>-                  C.__dict__['data'] may be a data descriptor with additional<br>-                  info, like a __doc__ string.<br>-      <br>-     Note: This version is patched to work with Zope Interface-bearing objects<br>-    """<br>+        3. The object as obtained directly from the defining class's<br>+           __dict__, not via getattr.  This is especially important for<br>+           data attributes:  C.data is just a data object, but<br>+           C.__dict__['data'] may be a data descriptor with additional<br>+           info, like a __doc__ string.<br> <br>-       mro = inspect.getmro(cls)<br>-    names = dir(cls)<br>-     result = []<br>-  for name in names:<br>-           # Get the object associated with the name.<br>-           # Getting an obj from the __dict__ sometimes reveals more than<br>-               # using getattr.  Static and class methods are dramatic examples.<br>-            if name in cls.__dict__:<br>-                     obj = cls.__dict__[name]<br>-             else:<br>-                        try:<br>-                         obj = getattr(cls, name)<br>-                     except AttributeError, err:<br>-                          continue<br>+    Note: This version is patched to work with Zope Interface-bearing objects<br>+    """<br> <br>-         # Figure out where it was defined.<br>-           homecls = getattr(obj, "__objclass__", None)<br>-               if homecls is None:<br>-                  # search the dicts.<br>-                  for base in mro:<br>-                             if name in base.__dict__:<br>-                                    homecls = base<br>-                                       break<br>+    mro = inspect.getmro(cls)<br>+    names = dir(cls)<br>+    result = []<br>+    for name in names:<br>+        # Get the object associated with the name.<br>+        # Getting an obj from the __dict__ sometimes reveals more than<br>+        # using getattr.  Static and class methods are dramatic examples.<br>+        if name in cls.__dict__:<br>+            obj = cls.__dict__[name]<br>+        else:<br>+            try:<br>+                obj = getattr(cls, name)<br>+            except AttributeError as err:<br>+                continue<br> <br>-          # Get the object again, in order to get it from the defining<br>-         # __dict__ instead of via getattr (if possible).<br>-             if homecls is not None and name in homecls.__dict__:<br>-                 obj = homecls.__dict__[name]<br>+        # Figure out where it was defined.<br>+        homecls = getattr(obj, "__objclass__", None)<br>+        if homecls is None:<br>+            # search the dicts.<br>+            for base in mro:<br>+                if name in base.__dict__:<br>+                    homecls = base<br>+                    break<br> <br>-            # Also get the object via getattr.<br>-           obj_via_getattr = getattr(cls, name)<br>+        # Get the object again, in order to get it from the defining<br>+        # __dict__ instead of via getattr (if possible).<br>+        if homecls is not None and name in homecls.__dict__:<br>+            obj = homecls.__dict__[name]<br> <br>-          # Classify the object.<br>-               if isinstance(obj, staticmethod):<br>-                    kind = "static method"<br>-             elif isinstance(obj, classmethod):<br>-                   kind = "class method"<br>-              elif isinstance(obj, property):<br>-                      kind = "property"<br>-          elif (inspect.ismethod(obj_via_getattr) or<br>-                     inspect.ismethoddescriptor(obj_via_getattr)):<br>-                      kind = "method"<br>-            else:<br>-                        kind = "data"<br>+        # Also get the object via getattr.<br>+        obj_via_getattr = getattr(cls, name)<br> <br>-               result.append((name, kind, homecls, obj))<br>+        # Classify the object.<br>+        if isinstance(obj, staticmethod):<br>+            kind = "static method"<br>+        elif isinstance(obj, classmethod):<br>+            kind = "class method"<br>+        elif isinstance(obj, property):<br>+            kind = "property"<br>+        elif (inspect.ismethod(obj_via_getattr) or<br>+              inspect.ismethoddescriptor(obj_via_getattr)):<br>+            kind = "method"<br>+        else:<br>+            kind = "data"<br> <br>-     return result<br>+        result.append((name, kind, homecls, obj))<br>+<br>+    return result<br> inspect.classify_class_attrs = classify_class_attrs<br> <br> <br> class DefaultFormatter(pydoc.HTMLDoc):<br>-        def docmodule(self, object, name=None, mod=None, packageContext = None, *ignored):<br>-           """Produce HTML documentation for a module object."""<br>-          name = object.__name__ # ignore the passed-in name<br>-           parts = split(name, '.')<br>-             links = []<br>-           for i in range(len(parts)-1):<br>-                        links.append(<br>-                                '<a href="%s.html"><font color="#ffffff">%s</font></a>' %<br>-                                (join(parts[:i+1], '.'), parts[i]))<br>-          linkedname = join(links + parts[-1:], '.')<br>-           head = '<big><big><strong>%s</strong></big></big>' % linkedname<br>-          try:<br>-                 path = inspect.getabsfile(object)<br>-                    url = path<br>-                   if sys.platform == 'win32':<br>-                          import nturl2path<br>-                            url = nturl2path.pathname2url(path)<br>-                  filelink = '<a href="file:%s">%s</a>' % (url, path)<br>-            except TypeError:<br>-                    filelink = '(built-in)'<br>-              info = []<br>-            if hasattr(object, '__version__'):<br>-                   version = str(object.__version__)<br>-                    if version[:11] == '$' + 'Revision: ' and version[-1:] == '$':<br>-                               version = strip(version[11:-1])<br>-                      info.append('version %s' % self.escape(version))<br>-             if hasattr(object, '__date__'):<br>-                      info.append(self.escape(str(object.__date__)))<br>-               if info:<br>-                     head = head + ' (%s)' % join(info, ', ')<br>-             result = self.heading(<br>-                       head, '#ffffff', '#7799ee', '<a href=".">index</a><br>' + filelink)<br>+    def docmodule(self, object, name=None, mod=None, packageContext=None, *ignored):<br>+        """Produce HTML documentation for a module object."""<br>+        name = object.__name__  # ignore the passed-in name<br>+        parts = split(name, '.')<br>+        links = []<br>+        for i in range(len(parts)-1):<br>+            links.append(<br>+                '<a href="%s.html"><font color="#ffffff">%s</font></a>' %<br>+                (join(parts[:i+1], '.'), parts[i]))<br>+        linkedname = join(links + parts[-1:], '.')<br>+        head = '<big><big><strong>%s</strong></big></big>' % linkedname<br>+        try:<br>+            path = inspect.getabsfile(object)<br>+            url = path<br>+            if sys.platform == 'win32':<br>+                import nturl2path<br>+                url = nturl2path.pathname2url(path)<br>+            filelink = '<a href="file:%s">%s</a>' % (url, path)<br>+        except TypeError:<br>+            filelink = '(built-in)'<br>+        info = []<br>+        if hasattr(object, '__version__'):<br>+            version = str(object.__version__)<br>+            if version[:11] == '$' + 'Revision: ' and version[-1:] == '$':<br>+                version = strip(version[11:-1])<br>+            info.append('version %s' % self.escape(version))<br>+        if hasattr(object, '__date__'):<br>+            info.append(self.escape(str(object.__date__)))<br>+        if info:<br>+            head = head + ' (%s)' % join(info, ', ')<br>+        result = self.heading(<br>+            head, '#ffffff', '#7799ee', '<a href=".">index</a><br>' + filelink)<br> <br>-              modules = inspect.getmembers(object, inspect.ismodule)<br>+        modules = inspect.getmembers(object, inspect.ismodule)<br> <br>-           classes, cdict = [], {}<br>-              for key, value in inspect.getmembers(object, inspect.isclass):<br>-                       if (inspect.getmodule(value) or object) is object:<br>-                           classes.append((key, value))<br>-                         cdict[key] = cdict[value] = '#' + key<br>-                for key, value in classes:<br>-                   for base in value.__bases__:<br>-                         key, modname = base.__name__, base.__module__<br>-                                module = sys.modules.get(modname)<br>-                            if modname != name and module and hasattr(module, key):<br>-                                      if getattr(module, key) is base:<br>-                                             if not cdict.has_key(key):<br>-                                                   cdict[key] = cdict[base] = modname + '.html#' + key<br>-          funcs, fdict = [], {}<br>-                for key, value in inspect.getmembers(object, inspect.isroutine):<br>-                     if inspect.isbuiltin(value) or inspect.getmodule(value) is object:<br>-                           funcs.append((key, value))<br>-                           fdict[key] = '#-' + key<br>-                              if inspect.isfunction(value): fdict[value] = fdict[key]<br>-              data = []<br>-            for key, value in inspect.getmembers(object, pydoc.isdata):<br>-                  if key not in ['__builtins__', '__doc__']:<br>-                           data.append((key, value))<br>+        classes, cdict = [], {}<br>+        for key, value in inspect.getmembers(object, inspect.isclass):<br>+            if (inspect.getmodule(value) or object) is object:<br>+                classes.append((key, value))<br>+                cdict[key] = cdict[value] = '#' + key<br>+        for key, value in classes:<br>+            for base in value.__bases__:<br>+                key, modname = base.__name__, base.__module__<br>+                module = sys.modules.get(modname)<br>+                if modname != name and module and hasattr(module, key):<br>+                    if getattr(module, key) is base:<br>+                        if key not in cdict:<br>+                            cdict[key] = cdict[base] = modname + '.html#' + key<br>+        funcs, fdict = [], {}<br>+        for key, value in inspect.getmembers(object, inspect.isroutine):<br>+            if inspect.isbuiltin(value) or inspect.getmodule(value) is object:<br>+                funcs.append((key, value))<br>+                fdict[key] = '#-' + key<br>+                if inspect.isfunction(value):<br>+                    fdict[value] = fdict[key]<br>+        data = []<br>+        for key, value in inspect.getmembers(object, pydoc.isdata):<br>+            if key not in ['__builtins__', '__doc__']:<br>+                data.append((key, value))<br> <br>-              doc = self.markup(pydoc.getdoc(object), self.preformat, fdict, cdict)<br>-                doc = doc and '<tt>%s</tt>' % doc<br>-                result = result + '<p>%s</p>\n' % doc<br>+        doc = self.markup(pydoc.getdoc(object), self.preformat, fdict, cdict)<br>+        doc = doc and '<tt>%s</tt>' % doc<br>+        result = result + '<p>%s</p>\n' % doc<br> <br>-             packageContext.clean ( classes, object )<br>-             packageContext.clean ( funcs, object )<br>-               packageContext.clean ( data, object )<br>-                <br>-             if hasattr(object, '__path__'):<br>-                      modpkgs = []<br>-                 modnames = []<br>-                        for file in os.listdir(object.__path__[0]):<br>-                          path = os.path.join(object.__path__[0], file)<br>-                                modname = inspect.getmodulename(file)<br>-                                if modname and modname not in modnames:<br>-                                      modpkgs.append((modname, name, 0, 0))<br>-                                        modnames.append(modname)<br>-                             elif pydoc.ispackage(path):<br>-                                  modpkgs.append((file, name, 1, 0))<br>-                   modpkgs.sort()<br>-                       contents = self.multicolumn(modpkgs, self.modpkglink)<br>-##                      result = result + self.bigsection(<br>-##                         'Package Contents', '#ffffff', '#aa55cc', contents)<br>-                  result = result + self.moduleSection( object, packageContext)<br>-                elif modules:<br>-                        contents = self.multicolumn(<br>-                         modules, lambda (key, value), s=self: s.modulelink(value))<br>-                   result = result + self.bigsection(<br>-                           'Modules', '#fffff', '#aa55cc', contents)<br>+        packageContext.clean(classes, object)<br>+        packageContext.clean(funcs, object)<br>+        packageContext.clean(data, object)<br> <br>-              <br>-             if classes:<br>-##                        print classes<br>-##                      import pdb<br>-##                 pdb.set_trace()<br>-                      classlist = map(lambda (key, value): value, classes)<br>-                 contents = [<br>-                         self.formattree(inspect.getclasstree(classlist, 1), name)]<br>-                   for key, value in classes:<br>-                           contents.append(self.document(value, key, name, fdict, cdict))<br>-                       result = result + self.bigsection(<br>-                           'Classes', '#ffffff', '#ee77aa', join(contents))<br>-             if funcs:<br>-                    contents = []<br>-                        for key, value in funcs:<br>-                             contents.append(self.document(value, key, name, fdict, cdict))<br>-                       result = result + self.bigsection(<br>-                           'Functions', '#ffffff', '#eeaa77', join(contents))<br>-           if data:<br>-                     contents = []<br>-                        for key, value in data:<br>-                              try:<br>-                                 contents.append(self.document(value, key))<br>-                           except Exception, err:<br>-                                       pass<br>-                 result = result + self.bigsection(<br>-                           'Data', '#ffffff', '#55aa55', join(contents, '<br>\n'))<br>-                if hasattr(object, '__author__'):<br>-                    contents = self.markup(str(object.__author__), self.preformat)<br>-                       result = result + self.bigsection(<br>-                           'Author', '#ffffff', '#7799ee', contents)<br>-            if hasattr(object, '__credits__'):<br>-                   contents = self.markup(str(object.__credits__), self.preformat)<br>-                      result = result + self.bigsection(<br>-                           'Credits', '#ffffff', '#7799ee', contents)<br>+        if hasattr(object, '__path__'):<br>+            modpkgs = []<br>+            modnames = []<br>+            for file in os.listdir(object.__path__[0]):<br>+                path = os.path.join(object.__path__[0], file)<br>+                modname = inspect.getmodulename(file)<br>+                if modname and modname not in modnames:<br>+                    modpkgs.append((modname, name, 0, 0))<br>+                    modnames.append(modname)<br>+                elif pydoc.ispackage(path):<br>+                    modpkgs.append((file, name, 1, 0))<br>+            modpkgs.sort()<br>+            contents = self.multicolumn(modpkgs, self.modpkglink)<br>+            result = result + self.moduleSection(object, packageContext)<br>+        elif modules:<br>+            contents = self.multicolumn(<br>+                modules, lambda kvp, s=self: s.modulelink(kvp[1]))<br>+            result = result + self.bigsection(<br>+                'Modules', '#fffff', '#aa55cc', contents)<br> <br>-                return result<br>+        if classes:<br>+            classlist = map(lambda kvp: kvp[1], classes)<br>+            contents = [<br>+                self.formattree(inspect.getclasstree(classlist, 1), name)]<br>+            for key, value in classes:<br>+                contents.append(self.document(value, key, name, fdict, cdict))<br>+            result = result + self.bigsection(<br>+                'Classes', '#ffffff', '#ee77aa', join(contents))<br>+        if funcs:<br>+            contents = []<br>+            for key, value in funcs:<br>+                contents.append(self.document(value, key, name, fdict, cdict))<br>+            result = result + self.bigsection(<br>+                'Functions', '#ffffff', '#eeaa77', join(contents))<br>+        if data:<br>+            contents = []<br>+            for key, value in data:<br>+                try:<br>+                    contents.append(self.document(value, key))<br>+                except Exception as err:<br>+                    pass<br>+            result = result + self.bigsection(<br>+                'Data', '#ffffff', '#55aa55', join(contents, '<br>\n'))<br>+        if hasattr(object, '__author__'):<br>+            contents = self.markup(str(object.__author__), self.preformat)<br>+            result = result + self.bigsection(<br>+                'Author', '#ffffff', '#7799ee', contents)<br>+        if hasattr(object, '__credits__'):<br>+            contents = self.markup(str(object.__credits__), self.preformat)<br>+            result = result + self.bigsection(<br>+                'Credits', '#ffffff', '#7799ee', contents)<br> <br>-     def classlink(self, object, modname):<br>-                """Make a link for a class."""<br>-         name, module = object.__name__, sys.modules.get(object.__module__)<br>-           if hasattr(module, name) and getattr(module, name) is object:<br>-                        return '<a href="%s.html#%s">%s</a>' % (<br>-                               module.__name__, name, name<br>-                  )<br>-            return pydoc.classname(object, modname)<br>-      <br>-     def moduleSection( self, object, packageContext ):<br>-           """Create a module-links section for the given object (module)"""<br>-              modules = inspect.getmembers(object, inspect.ismodule)<br>-               packageContext.clean ( modules, object )<br>-             packageContext.recurseScan( modules )<br>+        return result<br> <br>-             if hasattr(object, '__path__'):<br>-                      modpkgs = []<br>-                 modnames = []<br>-                        for file in os.listdir(object.__path__[0]):<br>-                          path = os.path.join(object.__path__[0], file)<br>-                                modname = inspect.getmodulename(file)<br>-                                if modname and modname not in modnames:<br>-                                      modpkgs.append((modname, object.__name__, 0, 0))<br>-                                     modnames.append(modname)<br>-                             elif pydoc.ispackage(path):<br>-                                  modpkgs.append((file, object.__name__, 1, 0))<br>-                        modpkgs.sort()<br>-                       # do more recursion here...<br>-                  for (modname, name, ya,yo) in modpkgs:<br>-                               packageContext.addInteresting( join( (object.__name__, modname), '.'))<br>-                       items = []<br>-                   for (modname, name, ispackage,isshadowed) in modpkgs:<br>-                                try:<br>-                                 # get the actual module object...<br>-##                                  if modname == "events":<br>-##                                          import pdb<br>-##                                         pdb.set_trace()<br>-                                      module = pydoc.safeimport( "%s.%s"%(name,modname) )<br>-                                        description, documentation = pydoc.splitdoc( inspect.getdoc( module ))<br>-                                       if description:<br>-                                              items.append(<br>-                                                        """%s -- %s"""% (<br>-                                                              self.modpkglink( (modname, name, ispackage, isshadowed) ),<br>-                                                           description,<br>-                                                 )<br>-                                            )<br>-                                    else:<br>-                                                items.append(<br>-                                                        self.modpkglink( (modname, name, ispackage, isshadowed) )<br>-                                            )<br>-                            except:<br>-                                      items.append(<br>-                                                self.modpkglink( (modname, name, ispackage, isshadowed) )<br>-                                    )<br>-                    contents = string.join( items, '<br>')<br>-                 result = self.bigsection(<br>-                            'Package Contents', '#ffffff', '#aa55cc', contents)<br>-          elif modules:<br>-                        contents = self.multicolumn(<br>-                         modules, lambda (key, value), s=self: s.modulelink(value))<br>-                   result = self.bigsection(<br>-                            'Modules', '#fffff', '#aa55cc', contents)<br>-            else:<br>-                        result = ""<br>-                return result<br>-        <br>-     <br>+    def classlink(self, object, modname):<br>+        """Make a link for a class."""<br>+        name, module = object.__name__, sys.modules.get(object.__module__)<br>+        if hasattr(module, name) and getattr(module, name) is object:<br>+            return '<a href="%s.html#%s">%s</a>' % (<br>+                module.__name__, name, name<br>+            )<br>+        return pydoc.classname(object, modname)<br>+<br>+    def moduleSection(self, object, packageContext):<br>+        """Create a module-links section for the given object (module)"""<br>+        modules = inspect.getmembers(object, inspect.ismodule)<br>+        packageContext.clean(modules, object)<br>+        packageContext.recurseScan(modules)<br>+<br>+        if hasattr(object, '__path__'):<br>+            modpkgs = []<br>+            modnames = []<br>+            for file in os.listdir(object.__path__[0]):<br>+                path = os.path.join(object.__path__[0], file)<br>+                modname = inspect.getmodulename(file)<br>+                if modname and modname not in modnames:<br>+                    modpkgs.append((modname, object.__name__, 0, 0))<br>+                    modnames.append(modname)<br>+                elif pydoc.ispackage(path):<br>+                    modpkgs.append((file, object.__name__, 1, 0))<br>+            modpkgs.sort()<br>+            # do more recursion here...<br>+            for (modname, name, ya, yo) in modpkgs:<br>+                packageContext.addInteresting(join((object.__name__, modname), '.'))<br>+            items = []<br>+            for (modname, name, ispackage, isshadowed) in modpkgs:<br>+                try:<br>+                    # get the actual module object...<br>+                    module = pydoc.safeimport("%s.%s" % (name, modname))<br>+                    description, documentation = pydoc.splitdoc(inspect.getdoc(module))<br>+                    if description:<br>+                        items.append(<br>+                            """%s -- %s""" % (<br>+                                self.modpkglink((modname, name, ispackage, isshadowed)),<br>+                                description,<br>+                            )<br>+                        )<br>+                    else:<br>+                        items.append(<br>+                            self.modpkglink((modname, name, ispackage, isshadowed))<br>+                        )<br>+                except:<br>+                    items.append(<br>+                        self.modpkglink((modname, name, ispackage, isshadowed))<br>+                    )<br>+            contents = string.join(items, '<br>')<br>+            result = self.bigsection(<br>+                'Package Contents', '#ffffff', '#aa55cc', contents)<br>+        elif modules:<br>+            contents = self.multicolumn(<br>+                modules, lambda kvp, s=self: s.modulelink(kvp[1]))<br>+            result = self.bigsection(<br>+                'Modules', '#fffff', '#aa55cc', contents)<br>+        else:<br>+            result = ""<br>+        return result<br>+<br>+<br> class AlreadyDone(Exception):<br>-      pass<br>- <br>+    pass<br> <br> <br> class PackageDocumentationGenerator:<br>-     """A package document generator creates documentation<br>- for an entire package using pydoc's machinery.<br>+    """A package document generator creates documentation<br>+    for an entire package using pydoc's machinery.<br> <br>-    baseModules -- modules which will be included<br>-                and whose included and children modules will be<br>-              considered fair game for documentation<br>-       destinationDirectory -- the directory into which<br>-             the HTML documentation will be written<br>-       recursion -- whether to add modules which are<br>-                referenced by and/or children of base modules<br>-        exclusions -- a list of modules whose contents will<br>-          not be shown in any other module, commonly<br>-           such modules as OpenGL.GL, wxPython.wx etc.<br>-  recursionStops -- a list of modules which will<br>-               explicitly stop recursion (i.e. they will never<br>-              be included), even if they are children of base<br>-              modules.<br>-     formatter -- allows for passing in a custom formatter<br>-                see DefaultFormatter for sample implementation.<br>-      """<br>-   def __init__ (<br>-               self, baseModules, destinationDirectory = ".",<br>-             recursion = 1, exclusions = (),<br>-              recursionStops = (),<br>-         formatter = None<br>-     ):<br>-           self.destinationDirectory = os.path.abspath( destinationDirectory)<br>-           self.exclusions = {}<br>-         self.warnings = []<br>-           self.baseSpecifiers = {}<br>-             self.completed = {}<br>-          self.recursionStops = {}<br>-             self.recursion = recursion<br>-           for stop in recursionStops:<br>-                  self.recursionStops[ stop ] = 1<br>-              self.pending = []<br>-            for exclusion in exclusions:<br>-                 try:<br>-                         self.exclusions[ exclusion ]= pydoc.locate ( exclusion)<br>-                      except pydoc.ErrorDuringImport, value:<br>-                               self.warn( """Unable to import the module %s which was specified as an exclusion module"""% (repr(exclusion)))<br>-         self.formatter = formatter or DefaultFormatter()<br>-             for base in baseModules:<br>-                     self.addBase( base )<br>- def warn( self, message ):<br>-           """Warnings are used for recoverable, but not necessarily ignorable conditions"""<br>-              self.warnings.append (message)<br>-       def info (self, message):<br>-            """Information/status report"""<br>-                print message<br>-        def addBase(self, specifier):<br>-                """Set the base of the documentation set, only children of these modules will be documented"""<br>-         try:<br>-                 self.baseSpecifiers [specifier] = pydoc.locate ( specifier)<br>-                  self.pending.append (specifier)<br>-              except pydoc.ErrorDuringImport, value:<br>-                       self.warn( """Unable to import the module %s which was specified as a base module"""% (repr(specifier)))<br>-       def addInteresting( self, specifier):<br>-                """Add a module to the list of interesting modules"""<br>-          if self.checkScope( specifier):<br>-##                    print "addInteresting", specifier<br>-                  self.pending.append (specifier)<br>-              else:<br>-                        self.completed[ specifier] = 1<br>-       def checkScope (self, specifier):<br>-            """Check that the specifier is "in scope" for the recursion"""<br>-               if not self.recursion:<br>-                       return 0<br>-             items = string.split (specifier, ".")<br>-              stopCheck = items [:]<br>-                while stopCheck:<br>-                     name = string.join(items, ".")<br>-                     if self.recursionStops.get( name):<br>-                           return 0<br>-                     elif self.completed.get (name):<br>-                              return 0<br>-                     del stopCheck[-1]<br>-            while items:<br>-                 if self.baseSpecifiers.get( string.join(items, ".")):<br>-                              return 1<br>-                     del items[-1]<br>-                # was not within any given scope<br>-             return 0<br>+    baseModules -- modules which will be included<br>+        and whose included and children modules will be<br>+        considered fair game for documentation<br>+    destinationDirectory -- the directory into which<br>+        the HTML documentation will be written<br>+    recursion -- whether to add modules which are<br>+        referenced by and/or children of base modules<br>+    exclusions -- a list of modules whose contents will<br>+        not be shown in any other module, commonly<br>+        such modules as OpenGL.GL, wxPython.wx etc.<br>+    recursionStops -- a list of modules which will<br>+        explicitly stop recursion (i.e. they will never<br>+        be included), even if they are children of base<br>+        modules.<br>+    formatter -- allows for passing in a custom formatter<br>+        see DefaultFormatter for sample implementation.<br>+    """<br>+    def __init__(<br>+        self, baseModules, destinationDirectory=".",<br>+        recursion=1, exclusions=(),<br>+        recursionStops=(),<br>+        formatter=None<br>+    ):<br>+        self.destinationDirectory = os.path.abspath(destinationDirectory)<br>+        self.exclusions = {}<br>+        self.warnings = []<br>+        self.baseSpecifiers = {}<br>+        self.completed = {}<br>+        self.recursionStops = {}<br>+        self.recursion = recursion<br>+        for stop in recursionStops:<br>+            self.recursionStops[stop] = 1<br>+        self.pending = []<br>+        for exclusion in exclusions:<br>+            try:<br>+                self.exclusions[exclusion] = pydoc.locate(exclusion)<br>+            except pydoc.ErrorDuringImport as value:<br>+                self.warn(<br>+                    ("Unable to import the module %s which "<br>+                     "was specified as an exclusion module") % (repr(exclusion)))<br>+        self.formatter = formatter or DefaultFormatter()<br>+        for base in baseModules:<br>+            self.addBase(base)<br> <br>-   def process( self ):<br>-         """Having added all of the base and/or interesting modules,<br>-           proceed to generate the appropriate documentation for each<br>-           module in the appropriate directory, doing the recursion<br>-             as we go."""<br>-          try:<br>-                 while self.pending:<br>-                          try:<br>-                                 if self.completed.has_key( self.pending[0] ):<br>-                                                raise AlreadyDone( self.pending[0] )<br>-                                 self.info( """Start %s"""% (repr(self.pending[0])))<br>-                                    object = pydoc.locate ( self.pending[0] )<br>-                                    self.info( """   ... found %s"""% (repr(object.__name__)))<br>-                             except AlreadyDone:<br>-                                  pass<br>-                         except pydoc.ErrorDuringImport, value:<br>-                                       self.info( """   ... FAILED %s"""% (repr( value)))<br>-                                     self.warn( """Unable to import the module %s"""% (repr(self.pending[0])))<br>-                              except (SystemError, SystemExit), value:<br>-                                     self.info( """   ... FAILED %s"""% (repr( value)))<br>-                                     self.warn( """Unable to import the module %s"""% (repr(self.pending[0])))<br>-                              except Exception, value:<br>-                                     self.info( """   ... FAILED %s"""% (repr( value)))<br>-                                     self.warn( """Unable to import the module %s"""% (repr(self.pending[0])))<br>-                              else:<br>-                                        page = self.formatter.page(<br>-                                          pydoc.describe(object),<br>-                                              self.formatter.docmodule(<br>-                                                    object,<br>-                                                      object.__name__,<br>-                                                     packageContext = self,<br>-                                               )<br>-                                    )<br>-                                    file = open (<br>-                                                os.path.join(<br>-                                                        self.destinationDirectory,<br>-                                                   self.pending[0] + ".html",<br>-                                         ),<br>-                                           'w',<br>-                                 )<br>-                                    file.write(page)<br>-                                     file.close()<br>-                                 self.completed[ self.pending[0]] = object<br>-                            del self.pending[0]<br>-          finally:<br>-                     for item in self.warnings:<br>-                           print item<br>-                   <br>-     def clean (self, objectList, object):<br>-                """callback from the formatter object asking us to remove<br>-             those items in the key, value pairs where the object is<br>-              imported from one of the excluded modules"""<br>-          for key, value in objectList[:]:<br>-                     for excludeObject in self.exclusions.values():<br>-                               if hasattr( excludeObject, key ) and excludeObject is not object:<br>-                                    if (<br>-                                         getattr( excludeObject, key) is value or<br>-                                             (hasattr( excludeObject, '__name__') and<br>-                                              excludeObject.__name__ == "Numeric"<br>-                                                )<br>-                                   ):<br>-                                           objectList[:] = [ (k,o) for k,o in objectList if k != key ]<br>-  def recurseScan(self, objectList):<br>-           """Process the list of modules trying to add each to the<br>-              list of interesting modules"""<br>-                for key, value in objectList:<br>-                        self.addInteresting( value.__name__ )<br>+    def warn(self, message):<br>+        """Warnings are used for recoverable, but not necessarily ignorable conditions"""<br>+        self.warnings.append(message)<br> <br>+    def info(self, message):<br>+        """Information/status report"""<br>+        print(message)<br>+<br>+    def addBase(self, specifier):<br>+        """Set the base of the documentation set, only children of these modules will<br>+        be documented"""<br>+        try:<br>+            self.baseSpecifiers[specifier] = pydoc.locate(specifier)<br>+            self.pending.append(specifier)<br>+        except pydoc.ErrorDuringImport as value:<br>+            self.warn(<br>+                "Unable to import the module %s which was specified as a base module" %<br>+                (repr(specifier)))<br>+<br>+    def addInteresting(self, specifier):<br>+        """Add a module to the list of interesting modules"""<br>+        if self.checkScope(specifier):<br>+            self.pending.append(specifier)<br>+        else:<br>+            self.completed[specifier] = 1<br>+<br>+    def checkScope(self, specifier):<br>+        """Check that the specifier is "in scope" for the recursion"""<br>+        if not self.recursion:<br>+            return 0<br>+        items = string.split(specifier, ".")<br>+        stopCheck = items[:]<br>+        while stopCheck:<br>+            name = string.join(items, ".")<br>+            if self.recursionStops.get(name):<br>+                return 0<br>+            elif self.completed.get(name):<br>+                return 0<br>+            del stopCheck[-1]<br>+        while items:<br>+            if self.baseSpecifiers.get(string.join(items, ".")):<br>+                return 1<br>+            del items[-1]<br>+        # was not within any given scope<br>+        return 0<br>+<br>+    def process(self):<br>+        """Having added all of the base and/or interesting modules,<br>+        proceed to generate the appropriate documentation for each<br>+        module in the appropriate directory, doing the recursion<br>+        as we go."""<br>+        try:<br>+            while self.pending:<br>+                try:<br>+                    if self.pending[0] in self.completed:<br>+                        raise AlreadyDone(self.pending[0])<br>+                    self.info("""Start %s""" % (repr(self.pending[0])))<br>+                    object = pydoc.locate(self.pending[0])<br>+                    self.info("""   ... found %s""" % (repr(object.__name__)))<br>+                except AlreadyDone:<br>+                    pass<br>+                except pydoc.ErrorDuringImport as value:<br>+                    self.info("   ... FAILED %s" % (repr(value)))<br>+                    self.warn("Unable to import the module %s" % (repr(self.pending[0])))<br>+                except (SystemError, SystemExit) as value:<br>+                    self.info("   ... FAILED %s" % (repr(value)))<br>+                    self.warn("Unable to import the module %s" % (repr(self.pending[0])))<br>+                except Exception as value:<br>+                    self.info("   ... FAILED %s" % (repr(value)))<br>+                    self.warn("Unable to import the module %s" % (repr(self.pending[0])))<br>+                else:<br>+                    page = self.formatter.page(<br>+                        pydoc.describe(object),<br>+                        self.formatter.docmodule(<br>+                            object,<br>+                            object.__name__,<br>+                            packageContext=self,<br>+                        )<br>+                    )<br>+                    file = open(<br>+                        os.path.join(<br>+                            self.destinationDirectory,<br>+                            self.pending[0] + ".html",<br>+                        ),<br>+                        'w',<br>+                    )<br>+                    file.write(page)<br>+                    file.close()<br>+                    self.completed[self.pending[0]] = object<br>+                del self.pending[0]<br>+        finally:<br>+            for item in self.warnings:<br>+                print(item)<br>+<br>+    def clean(self, objectList, object):<br>+        """callback from the formatter object asking us to remove<br>+        those items in the key, value pairs where the object is<br>+        imported from one of the excluded modules"""<br>+        for key, value in objectList[:]:<br>+            for excludeObject in self.exclusions.values():<br>+                if hasattr(excludeObject, key) and excludeObject is not object:<br>+                    if (<br>+                        getattr(excludeObject, key) is value or<br>+                        (hasattr(excludeObject, '__name__') and<br>+                         excludeObject.__name__ == "Numeric"<br>+                         )<br>+                    ):<br>+                        objectList[:] = [(k, o) for k, o in objectList if k != key]<br>+<br>+    def recurseScan(self, objectList):<br>+        """Process the list of modules trying to add each to the<br>+        list of interesting modules"""<br>+        for key, value in objectList:<br>+            self.addInteresting(value.__name__)<br> <br> <br> if __name__ == "__main__":<br>-   excludes = [<br>-         "OpenGL.GL",<br>-               "OpenGL.GLU",<br>-              "OpenGL.GLUT",<br>-             "OpenGL.GLE",<br>-              "OpenGL.GLX",<br>-              "wxPython.wx",<br>-             "Numeric",<br>-         "_tkinter",<br>-                "Tkinter",<br>- ]<br>+    excludes = [<br>+        "OpenGL.GL",<br>+        "OpenGL.GLU",<br>+        "OpenGL.GLUT",<br>+        "OpenGL.GLE",<br>+        "OpenGL.GLX",<br>+        "wxPython.wx",<br>+        "Numeric",<br>+        "_tkinter",<br>+        "Tkinter",<br>+    ]<br> <br>-   modules = [<br>-          "OpenGLContext.debug",<br>-##           "wxPython.glcanvas",<br>-##             "OpenGL.Tk",<br>-##             "OpenGL",<br>-  ]       <br>-     PackageDocumentationGenerator(<br>-               baseModules = modules,<br>-               destinationDirectory = "z:\\temp",<br>-         exclusions = excludes,<br>-       ).process ()<br>-         <br>+    modules = [<br>+        "OpenGLContext.debug",<br>+    ]<br>+    PackageDocumentationGenerator(<br>+        baseModules=modules,<br>+        destinationDirectory="z:\\temp",<br>+        exclusions=excludes,<br>+    ).process()<br>diff --git a/examples/amicommand.py b/examples/amicommand.py<br>index ee0e7b1..04ac913 100644<br>--- a/examples/amicommand.py<br>+++ b/examples/amicommand.py<br>@@ -1,35 +1,38 @@<br> #! /usr/bin/env python<br> """Test/sample to call "show database" command<br> """<br>-from twisted.application import service, internet<br>-from twisted.internet import reactor, defer<br>-from starpy import manager, fastagi<br>+from twisted.internet import reactor<br>+from starpy import manager<br> import utilapplication<br>-import menu<br>-import os, logging, pprint, time<br>+import logging<br> <br>-log = logging.getLogger( 'callduration' )<br>+log = logging.getLogger('callduration')<br> APPLICATION = utilapplication.UtilApplication()<br> <br>+<br> def main():<br>-    def onConnect( ami ):<br>-        def onResult( result ):<br>-            print 'Result', result<br>+<br>+    def onConnect(ami):<br>+<br>+        def onResult(result):<br>+            print('Result', result)<br>             return ami.logoff()<br>-        def onError( reason ):<br>-            print reason.getTraceback()<br>+<br>+        def onError(reason):<br>+            print(reason.getTraceback())<br>             return reason<br>-        def onFinished( result ):<br>+<br>+        def onFinished(result):<br>             reactor.stop()<br>-        df = ami.command( 'database show' )<br>-        df.addCallbacks( onResult, onError )<br>-        df.addCallbacks( onFinished, onFinished )<br>+        df = ami.command('database show')<br>+        df.addCallbacks(onResult, onError)<br>+        df.addCallbacks(onFinished, onFinished)<br>         return df<br>-    amiDF = APPLICATION.amiSpecifier.login(<br>-    ).addCallback( onConnect )<br>+    APPLICATION.amiSpecifier.login().addCallback(onConnect)<br>+<br> <br> if __name__ == "__main__":<br>     logging.basicConfig()<br>-    manager.log.setLevel( logging.DEBUG )<br>-    reactor.callWhenRunning( main )<br>+    manager.log.setLevel(logging.DEBUG)<br>+    reactor.callWhenRunning(main)<br>     reactor.run()<br>diff --git a/examples/autosurvey/frontend.py b/examples/autosurvey/frontend.py<br>index da50378..7843a34 100644<br>--- a/examples/autosurvey/frontend.py<br>+++ b/examples/autosurvey/frontend.py<br>@@ -1,167 +1,176 @@<br> """Simple HTTP Server using twisted.web2"""<br>-from nevow import rend, appserver, inevow, tags, loaders<br>-from twisted.application import service, internet<br>-from twisted.internet import reactor, defer<br>+from nevow import rend, appserver, inevow, loaders<br>+from twisted.application import internet<br>+from twisted.internet import reactor<br> from starpy import manager, fastagi, utilapplication<br> from basicproperty import common, basic, propertied, weak<br>-import os, logging, pprint, time<br>+import logging<br>+import random<br>+import sys<br> <br>-log = logging.getLogger( 'autosurvey' )<br>-<br>-class Application( utilapplication.UtilApplication ):<br>-       """Services provided at the application level"""<br>-       surveys = common.DictionaryProperty(<br>-         "surveys", """Set of surveys indexed by survey/extension number""",<br>-  )<br>+log = logging.getLogger('autosurvey')<br> <br> <br>+class Application(utilapplication.UtilApplication):<br>+    """Services provided at the application level"""<br>+    surveys = common.DictionaryProperty(<br>+        "surveys", """Set of surveys indexed by survey/extension number""",<br>+    )<br> <br>-class Survey( propertied.Propertied ):<br>-      """Models a single survey to be completed"""<br>-   surveyId = common.IntegerProperty(<br>-           "surveyId", """Unique identifier for this survey""",<br>- )<br>-    owner = basic.BasicProperty(<br>-         "owner", """Owner's phone number to which to connect""",<br>- )<br>-    questions = common.ListProperty(<br>-             "questions", """Set of questions which make up the survey""",<br>-        )<br>-    YOU_CURRENTLY_HAVE = 'vm-youhave'<br>-    QUESTIONS_IN_YOUR_SURVEY = 'vm-messages'<br>-     QUESTION_IN_YOUR_SURVEY = 'vm-message'<br>-       TO_LISTEN_TO_SURVEY_QUESTION = 'to-listen-to-it'<br>-     TO_RECORD_A_NEW_SURVEY_QUESTION = 'to-rerecord-it'<br>-   TO_FINISH_SURVEY_SETUP = 'vm-helpexit'<br>-       def setupSurvey( self, agi ):<br>-                """AGI application to allow the user to set up the survey<br>-             <br>-             Screen 1:<br>-                    You have # questions.<br>-                        To listen to a question, press the number of the question.<br>-                   To record a new question, press pound.<br>-                       To finish setup, press star.<br>-         """<br>-           seq = fastagi.InSequence( )<br>-          seq.append( agi.wait, 2 )<br>-            base = """You currently have %s question%s.<br>-           To listen to a question press the number of the question.<br>-            To record a new question, press pound.<br>-               To finish survey setup, press star.<br>-          """%(<br>-                 len(self.questions),<br>-                 ['','s'][len(self.questions)==1],<br>-            )<br>-            if len(base) != 1:<br>-                   base += 's'<br>-          base = " ".join(base.split())<br>-              seq.append( agi.execute, 'Festival', base )<br>-          seq.append( agi.finish, )<br>-            return seq()<br>-         seq.append( agi.streamFile, self.YOU_CURRENTLY_HAVE )<br>-                seq.append( agi.sayNumber, len(self.questions))<br>-              if len(self.questions) == 1:<br>-                 seq.append( agi.streamFile, self.QUESTION_IN_YOUR_SURVEY )<br>-           else:<br>-                        seq.append( agi.streamFile, self.QUESTIONS_IN_YOUR_SURVEY )<br>-          seq.append( agi.streamFile, self.TO_LISTEN_TO_SURVEY_QUESTION )<br>-              seq.append( agi.streamFile, self.TO_RECORD_A_NEW_SURVEY_QUESTION )<br>-           seq.append( agi.streamFile, self.TO_FINISH_SURVEY_SETUP )<br>-            seq.append( agi.finish, )<br>-            return seq()<br>- def newQuestionId( self ):<br>-           """Return a new, unique, question id"""<br>-                import random, sys<br>-           bad = True<br>-           while bad:<br>-                   bad = False<br>-                  id = random.randint(0,sys.maxint)<br>-                    for question in self.questions:<br>-                              if id == question.__dict__.get('questionId'):<br>-                                        bad = True<br>-           return id<br>-class Question( propertied.Propertied ):<br>- survey = weak.WeakProperty(<br>-          "survey", """Our survey object""",<br>-   )<br>-    questionId = common.IntegerProperty(<br>-         "questionId", """Unique identifier for our question""",<br>-              defaultFunction = lambda prop,client: client.survey.newQuestionId(),<br>- )<br>-    def recordQuestion( self, agi, number=None ):<br>-                """Record a question (number)"""<br>-               return agi.recordFile( <br>-                      '%s.%s'%(self.survey.surveyId,self.questionId),<br>-                      'gsm',<br>-                       '#*',<br>-                        timeout=60,<br>-                  beep = True,<br>-                 silence=5,<br>-           ).addCallback( <br>-                      self.onRecorded, agi=agi<br>-             ).addErrback(self.onRecordAborted, agi=agi )<br>- def onRecorded( self, result, agi ):<br>-         """Handle recording of the question"""<br>-         <br> <br>-def getManagerAPI( username, password, server='127.0.0.1', port=5038 ):<br>-        """Retrieve a logged-in manager API"""<br>+class Survey(propertied.Propertied):<br>+    """Models a single survey to be completed"""<br>+    surveyId = common.IntegerProperty(<br>+        "surveyId", """Unique identifier for this survey""",<br>+    )<br>+    owner = basic.BasicProperty(<br>+        "owner", """Owner's phone number to which to connect""",<br>+    )<br>+    questions = common.ListProperty(<br>+        "questions", """Set of questions which make up the survey""",<br>+    )<br>+    YOU_CURRENTLY_HAVE = 'vm-youhave'<br>+    QUESTIONS_IN_YOUR_SURVEY = 'vm-messages'<br>+    QUESTION_IN_YOUR_SURVEY = 'vm-message'<br>+    TO_LISTEN_TO_SURVEY_QUESTION = 'to-listen-to-it'<br>+    TO_RECORD_A_NEW_SURVEY_QUESTION = 'to-rerecord-it'<br>+    TO_FINISH_SURVEY_SETUP = 'vm-helpexit'<br>+<br>+    def setupSurvey(self, agi):<br>+        """AGI application to allow the user to set up the survey<br>+<br>+        Screen 1:<br>+            You have # questions.<br>+            To listen to a question, press the number of the question.<br>+            To record a new question, press pound.<br>+            To finish setup, press star.<br>+        """<br>+        seq = fastagi.InSequence()<br>+        seq.append(agi.wait, 2)<br>+        base = """You currently have %s question%s.<br>+        To listen to a question press the number of the question.<br>+        To record a new question, press pound.<br>+        To finish survey setup, press star.<br>+        """ % (<br>+            len(self.questions),<br>+            ['', 's'][len(self.questions) == 1],<br>+        )<br>+        if len(base) != 1:<br>+            base += 's'<br>+        base = " ".join(base.split())<br>+        seq.append(agi.execute, 'Festival', base)<br>+        seq.append(agi.finish,)<br>+        return seq()<br>+        seq.append(agi.streamFile, self.YOU_CURRENTLY_HAVE)<br>+        seq.append(agi.sayNumber, len(self.questions))<br>+        if len(self.questions) == 1:<br>+            seq.append(agi.streamFile, self.QUESTION_IN_YOUR_SURVEY)<br>+        else:<br>+            seq.append(agi.streamFile, self.QUESTIONS_IN_YOUR_SURVEY)<br>+        seq.append(agi.streamFile, self.TO_LISTEN_TO_SURVEY_QUESTION)<br>+        seq.append(agi.streamFile, self.TO_RECORD_A_NEW_SURVEY_QUESTION)<br>+        seq.append(agi.streamFile, self.TO_FINISH_SURVEY_SETUP)<br>+        seq.append(agi.finish,)<br>+        return seq()<br>+<br>+    def newQuestionId(self):<br>+        """Return a new, unique, question id"""<br>+        bad = True<br>+        while bad:<br>+            bad = False<br>+            id = random.randint(0, sys.maxint)<br>+            for question in self.questions:<br>+                if id == question.__dict__.get('questionId'):<br>+                    bad = True<br>+        return id<br>+<br>+<br>+class Question(propertied.Propertied):<br>+    survey = weak.WeakProperty(<br>+        "survey", """Our survey object""",<br>+    )<br>+    questionId = common.IntegerProperty(<br>+        "questionId", """Unique identifier for our question""",<br>+        defaultFunction=lambda prop, client: client.survey.newQuestionId(),<br>+    )<br>+<br>+    def recordQuestion(self, agi, number=None):<br>+        """Record a question (number)"""<br>+        return agi.recordFile(<br>+            '%s.%s' % (self.survey.surveyId, self.questionId),<br>+            'gsm',<br>+            '#*',<br>+            timeout=60,<br>+            beep=True,<br>+            silence=5,<br>+        ).addCallback(<br>+            self.onRecorded, agi=agi<br>+        ).addErrback(self.onRecordAborted, agi=agi)<br>+<br>+    def onRecorded(self, result, agi):<br>+        """Handle recording of the question"""<br>+        pass<br>+<br>+<br>+def getManagerAPI(username, password, server='127.0.0.1', port=5038):<br>+    """Retrieve a logged-in manager API"""<br>+<br> <br> class SurveySetup(rend.Page):<br>-    """Page displaying the survey setup"""<br>- addSlash = True<br>-      docFactory = loaders.htmlfile( 'index.html' )<br>-<br>-class RecordFunction( rend.Page ):<br>-        """Page/application to record survey via call to user"""<br>-       def renderHTTP( self, ctx ):<br>-         """Process rendering of the request"""<br>-         # process request parameters...<br>-              request = inevow.IRequest( ctx )<br>-             # XXX sanitise and check value...<br>-            channel = 'SIP/%s'%( request.args['ownerName'][0], )<br>-         <br>-             df = APPLICATION.amiSpecifier.login()<br>-                def onLogin( ami ):<br>-                  # Note that the connect comes in *before* the originate returns,<br>-                     # so we need to wait for the call before we even send it...<br>-                  userConnectDF = APPLICATION.waitForCallOn( '23', timeout=15 )<br>-                        APPLICATION.surveys['23'] = survey = Survey()<br>-                        userConnectDF.addCallback( <br>-                          survey.setupSurvey, <br>-                 )<br>-                    def onComplete( result ):<br>-                            return ami.logoff()<br>-                  ami.originate(# don't wait for this to complete...<br>-                               # XXX handle case where the originate fails differently<br>-                              # from the case where we just don't get a connection?<br>-                            channel,<br>-                             APPLICATION.agiSpecifier.context,<br>-                            '23',<br>-                                '1',<br>-                         timeout=14,<br>-                  ).addCallbacks( onComplete, onComplete )<br>-                     return userConnectDF<br>-         return df.addCallback( onLogin )<br>+    """Page displaying the survey setup"""<br>+    addSlash = True<br>+    docFactory = loaders.htmlfile('index.html')<br> <br> <br>+class RecordFunction(rend.Page):<br>+    """Page/application to record survey via call to user"""<br>+    def renderHTTP(self, ctx):<br>+        """Process rendering of the request"""<br>+        # process request parameters...<br>+        request = inevow.IRequest(ctx)<br>+        # XXX sanitise and check value...<br>+        channel = 'SIP/%s' % (request.args['ownerName'][0],)<br>+<br>+        df = APPLICATION.amiSpecifier.login()<br>+<br>+        def onLogin(ami):<br>+            # Note that the connect comes in *before* the originate returns,<br>+            # so we need to wait for the call before we even send it...<br>+            userConnectDF = APPLICATION.waitForCallOn('23', timeout=15)<br>+            APPLICATION.surveys['23'] = survey = Survey()<br>+            userConnectDF.addCallback(survey.setupSurvey,)<br>+<br>+            def onComplete(result):<br>+                return ami.logoff()<br>+            ami.originate(<br>+                # don't wait for this to complete...<br>+                # XXX handle case where the originate fails differently<br>+                # from the case where we just don't get a connection?<br>+                channel,<br>+                APPLICATION.agiSpecifier.context,<br>+                '23',<br>+                '1',<br>+                timeout=14,<br>+            ).addCallbacks(onComplete, onComplete)<br>+            return userConnectDF<br>+        return df.addCallback(onLogin)<br> <br> <br> def main():<br>-  """Create the web-site"""<br>-      s = SurveySetup()<br>-    s.putChild( 'record', RecordFunction() )<br>-     site = appserver.NevowSite(s)<br>-        webServer = internet.TCPServer(8080, site)<br>-   webServer.startService()<br>+    """Create the web-site"""<br>+    s = SurveySetup()<br>+    s.putChild('record', RecordFunction())<br>+    site = appserver.NevowSite(s)<br>+    webServer = internet.TCPServer(8080, site)<br>+    webServer.startService()<br>+<br> <br> if __name__ == "__main__":<br>- logging.basicConfig()<br>-        log.setLevel( logging.DEBUG )<br>-        manager.log.setLevel( logging.DEBUG )<br>-        fastagi.log.setLevel( logging.DEBUG )<br>-        APPLICATION = Application()<br>-  APPLICATION.agiSpecifier.run( APPLICATION.dispatchIncomingCall )<br>-     from twisted.internet import reactor<br>- reactor.callWhenRunning( main )<br>-      reactor.run()<br>+    logging.basicConfig()<br>+    log.setLevel(logging.DEBUG)<br>+    manager.log.setLevel(logging.DEBUG)<br>+    fastagi.log.setLevel(logging.DEBUG)<br>+    APPLICATION = Application()<br>+    APPLICATION.agiSpecifier.run(APPLICATION.dispatchIncomingCall)<br>+    reactor.callWhenRunning(main)<br>+    reactor.run()<br>diff --git a/examples/calldurationcallback.py b/examples/calldurationcallback.py<br>index 00c8260..6ed6cc3 100644<br>--- a/examples/calldurationcallback.py<br>+++ b/examples/calldurationcallback.py<br>@@ -6,26 +6,29 @@<br> for the end of the call, then call them back with the<br> duration message.<br> """<br>-from twisted.application import service, internet<br> from twisted.internet import reactor, defer<br>-from starpy import manager, fastagi<br>+from starpy import fastagi<br> import utilapplication<br> import menu<br>-import os, logging, pprint, time<br>+import logging<br>+import time<br> <br>-log = logging.getLogger( 'callduration' )<br>+log = logging.getLogger('callduration')<br> <br>-class Application( utilapplication.UtilApplication ):<br>+<br>+class Application(utilapplication.UtilApplication):<br>     """Application for the call duration callback mechanism"""<br>-    def onS( self, agi ):<br>+<br>+    def onS(self, agi):<br>         """Incoming AGI connection to the "s" extension (start operation)"""<br>-        log.info( """New call tracker""" )<br>+        log.info("""New call tracker""")<br>         c = CallTracker()<br>-        return c.recordChannelInfo( agi ).addErrback(<br>+        return c.recordChannelInfo(agi).addErrback(<br>             agi.jumpOnError, difference=100,<br>         )<br> <br>-class CallTracker( object ):<br>+<br>+class CallTracker(object):<br>     """Object which tracks duration of a single call<br> <br>     This object encapsulates the entire interaction with the user, from<br>@@ -37,7 +40,8 @@<br>     as all numeric extensions.<br>     """<br>     ourContext = 'callduration'<br>-    def __init__( self ):<br>+<br>+    def __init__(self):<br>         """Initialise the tracker object"""<br>         self.uniqueChannelId = None<br>         self.currentChannel = None<br>@@ -47,135 +51,151 @@<br>         self.ami = None<br>         self.startTime = None<br>         self.stopTime = None<br>-    def recordChannelInfo( self, agi ):<br>+<br>+    def recordChannelInfo(self, agi):<br>         """Records relevant channel information, creates manager watcher"""<br>         self.uniqueChannelId = agi.variables['agi_uniqueid']<br>         self.currentChannel = currentChannel = agi.variables['agi_channel']<br>-        # XXX everything up to the last - is normally our local caller's "address"<br>+        # XXX everything up to the last - normally our local caller's "address"<br>         # this is not, however, a great way to decide who to call back...<br>-        self.callbackChannel = currentChannel.rsplit( '-', 1)[0]<br>+        self.callbackChannel = currentChannel.rsplit('-', 1)[0]<br>         # Ask user for the account number...<br>         df = menu.CollectDigits(<br>-            soundFile = 'your-account',<br>-            maxDigits = 7,<br>-            minDigits = 3,<br>-            timeout = 5,<br>-        )( agi ).addCallback(<br>-            self.onAccountInput,agi=agi,<br>+            soundFile='your-account',<br>+            maxDigits=7,<br>+            minDigits=3,<br>+            timeout=5,<br>+        )(agi).addCallback(<br>+            self.onAccountInput, agi=agi,<br>         )<br>         # XXX handle AMI login failure...<br>         amiDF = APPLICATION.amiSpecifier.login(<br>-        ).addCallback( self.onAMIConnect )<br>-        dl = defer.DeferredList( [df, amiDF] )<br>-        return dl.addCallback( self.onConnectAndAccount )<br>-    def onAccountInput( self, result, agi, retries=2):<br>+        ).addCallback(self.onAMIConnect)<br>+        dl = defer.DeferredList([df, amiDF])<br>+        return dl.addCallback(self.onConnectAndAccount)<br>+<br>+    def onAccountInput(self, result, agi, retries=2):<br>         """Allow user to enter again if timed out"""<br>         self.account = result[0][1]<br>         self.startTime = time.time()<br>-        agi.finish() # let the user go about their business...<br>+        agi.finish()  # let the user go about their business...<br>         return agi<br>-    def cleanUp( self, agi=None ):<br>+<br>+    def cleanUp(self, agi=None):<br>         """Cleanup on error as much as possible"""<br>         items = []<br>         if self.ami:<br>-            items.append( self.ami.logoff())<br>+            items.append(self.ami.logoff())<br>             self.ami = None<br>         if items:<br>-            return defer.DeferredList( items )<br>+            return defer.DeferredList(items)<br>         else:<br>-            return defer.succeed( False )<br>-    def onAMIConnect( self, ami ):<br>+            return defer.succeed(False)<br>+<br>+    def onAMIConnect(self, ami):<br>         """We have successfully connected to the AMI"""<br>-        log.debug( "AMI login complete" )<br>+        log.debug("AMI login complete")<br>         if not self.cancelled:<br>             self.ami = ami<br>             return ami<br>         else:<br>             return self.ami.logoff()<br>-    def onConnectAndAccount( self, results ):<br>+<br>+    def onConnectAndAccount(self, results):<br>         """We have connected and retrieved an account"""<br>-        log.info( """AMI Connected and account information gathered: %s""", self.uniqueChannelId )<br>+        log.info("""AMI Connected and account information gathered: %s""",<br>+                 self.uniqueChannelId)<br>         df = defer.Deferred()<br>-        def onChannelHangup( ami, event ):<br>+<br>+        def onChannelHangup(ami, event):<br>             """Deal with the hangup of an event"""<br>             if event['uniqueid'] == self.uniqueChannelId:<br>-                log.info( """AMI Detected close of our channel: %s""", self.uniqueChannelId )<br>+                log.info("""AMI Detected close of our channel: %s""",<br>+                         self.uniqueChannelId)<br>                 self.stopTime = time.time()<br>                 # give the user a few seconds to put down the hand-set<br>-                reactor.callLater( 2, df.callback, event )<br>-                self.ami.deregisterEvent( 'Hangup', onChannelHangup )<br>-            log.debug( 'event:', event )<br>+                reactor.callLater(2, df.callback, event)<br>+                self.ami.deregisterEvent('Hangup', onChannelHangup)<br>+            log.debug('event:', event)<br>         if not self.cancelled:<br>-            self.ami.registerEvent( 'Hangup', onChannelHangup )<br>-            return df.addCallback( self.onHangup, callbacks=5 )<br>-    def onHangup( self, event, callbacks=5 ):<br>+            self.ami.registerEvent('Hangup', onChannelHangup)<br>+            return df.addCallback(self.onHangup, callbacks=5)<br>+<br>+    def onHangup(self, event, callbacks=5):<br>         """Okay, the call is finished, time to inform the user"""<br>-        log.debug( 'onHangup %s %s', event, callbacks )<br>-        def ignoreResult( result ):<br>+        log.debug('onHangup %s %s', event, callbacks)<br>+<br>+        def ignoreResult(result):<br>             """Since we're using an equal timeout waiting for a connect<br>             we don't care *how* this fails/succeeds"""<br>             pass<br>         self.ami.originate(<br>             self.callbackChannel,<br>             self.ourContext, id(self), 1,<br>-            timeout = 15,<br>-        ).addCallbacks( ignoreResult, ignoreResult )<br>-        df = APPLICATION.waitForCallOn( id(self), 15 )<br>+            timeout=15,<br>+        ).addCallbacks(ignoreResult, ignoreResult)<br>+        df = APPLICATION.waitForCallOn(id(self), 15)<br>         df.addCallbacks(<br>             self.onUserReconnected, self.onUserReconnectFail,<br>-            errbackKeywords = { 'event': event, 'callbacks': callbacks-1 },<br>+            errbackKeywords={'event': event, 'callbacks': callbacks-1},<br>         )<br>-    def onUserReconnectFail( self, reason, event, callbacks ):<br>+<br>+    def onUserReconnectFail(self, reason, event, callbacks):<br>         """Wait for bit, then retry..."""<br>         if callbacks:<br>             # XXX really want something like a decaying back-off in frequency<br>             # with final values of e.g. an hour...<br>-            log.info( """Failure connecting: will retry in 30 seconds""" )<br>-            reactor.callLater( 30, self.onHangup, event, callbacks )<br>+            log.info("""Failure connecting: will retry in 30 seconds""")<br>+            reactor.callLater(30, self.onHangup, event, callbacks)<br>         else:<br>-            log.error( """Unable to connect to user, giving up""" )<br>-            return self.cleanUp( None )<br>-    def onUserReconnected( self, agi ):<br>+            log.error("""Unable to connect to user, giving up""")<br>+            return self.cleanUp(None)<br>+<br>+    def onUserReconnected(self, agi):<br>         """Handle the user interaction after they've re-connected"""<br>-        log.info( """Connection re-established with the user""" )<br>+        log.info("""Connection re-established with the user""")<br>         # XXX should handle unexpected failures in here...<br>         delta = self.stopTime - self.startTime<br>-        minutes, seconds = divmod( delta, 60 )<br>+        minutes, seconds = divmod(delta, 60)<br>         seconds = int(seconds)<br>-        hours, minutes = divmod( minutes, 60 )<br>+        hours, minutes = divmod(minutes, 60)<br>         duration = []<br>         if hours:<br>-            duration.append( '%s hour%s'%(hours,['','s'][hours!=1]))<br>+            duration.append('%s hour%s' % (hours, ['', 's'][hours != 1]))<br>         if minutes:<br>-            duration.append( '%s second%s'%(minutes,['','s'][minutes!=1]))<br>+            duration.append('%s second%s' % (minutes, ['', 's'][minutes != 1]))<br>         if seconds:<br>-            duration.append( '%s second%s'%(seconds,['','s'][seconds!=1]))<br>+            duration.append('%s second%s' % (seconds, ['', 's'][seconds != 1]))<br>         if not duration:<br>             duration = '0'<br>         else:<br>-            duration = " ".join( duration )<br>-        seq = fastagi.InSequence( )<br>-        seq.append( agi.wait, 1 )<br>-        seq.append( agi.execute, "Festival", "Call to account %r took %s"%(self.account,duration) )<br>-        seq.append( agi.wait, 1 )<br>-        seq.append( agi.execute, "Festival", "Repeating, call to account %r took %s"%(self.account,duration) )<br>-        seq.append( agi.wait, 1 )<br>-        seq.append( agi.finish )<br>-        def logSuccess( ):<br>-            log.debug( """Finished successfully!""" )<br>-            return defer.succeed( True )<br>-        seq.append( logSuccess )<br>-        seq.append( self.cleanUp, agi )<br>+            duration = " ".join(duration)<br>+        seq = fastagi.InSequence()<br>+        seq.append(agi.wait, 1)<br>+        seq.append(agi.execute, "Festival", "Call to account %r took %s" % (<br>+                   self.account, duration))<br>+        seq.append(agi.wait, 1)<br>+        seq.append(agi.execute, "Festival",<br>+                   "Repeating, call to account %r took %s" % (<br>+                       self.account, duration))<br>+        seq.append(agi.wait, 1)<br>+        seq.append(agi.finish)<br>+<br>+        def logSuccess():<br>+            log.debug("""Finished successfully!""")<br>+            return defer.succeed(True)<br>+        seq.append(logSuccess)<br>+        seq.append(self.cleanUp, agi)<br>         return seq()<br>+<br> <br> APPLICATION = Application()<br> <br> if __name__ == "__main__":<br>     logging.basicConfig()<br>-    log.setLevel( logging.DEBUG )<br>-    #manager.log.setLevel( logging.DEBUG )<br>-    #fastagi.log.setLevel( logging.DEBUG )<br>-    APPLICATION.handleCallsFor( 's', APPLICATION.onS )<br>-    APPLICATION.agiSpecifier.run( APPLICATION.dispatchIncomingCall )<br>-    from twisted.internet import reactor<br>+    log.setLevel(logging.DEBUG)<br>+    # manager.log.setLevel(logging.DEBUG)<br>+    # fastagi.log.setLevel(logging.DEBUG)<br>+    APPLICATION.handleCallsFor('s', APPLICATION.onS)<br>+    APPLICATION.agiSpecifier.run(APPLICATION.dispatchIncomingCall)<br>     reactor.run()<br>diff --git a/examples/connecttoivr.py b/examples/connecttoivr.py<br>index 24dcf63..3790390 100644<br>--- a/examples/connecttoivr.py<br>+++ b/examples/connecttoivr.py<br>@@ -1,38 +1,46 @@<br> """Example script to generate a call to connect a remote channel to an IVR"""<br> from starpy import manager<br> from twisted.internet import reactor<br>-import sys, logging<br>+import sys<br>+import logging<br> <br>-def main( channel = 'sip/20035@aci.on.ca', connectTo=('outgoing','s','1') ):<br>-   f = manager.AMIFactory(sys.argv[1], sys.argv[2])<br>-     df = f.login()<br>-       def onLogin( protocol ):<br>-             """On Login, attempt to originate the call"""<br>-          context, extension, priority = connectTo<br>-             df = protocol.originate(<br>-                     channel,<br>-                     context,extension,priority,<br>-          )<br>-            def onFinished( result ):<br>-                    df = protocol.logoff()<br>-                       def onLogoff( result ):<br>-                              reactor.stop()<br>-                       return df.addCallbacks( onLogoff, onLogoff )<br>-         def onFailure( reason ):<br>-                     print reason.getTraceback()<br>-                  return reason <br>-               df.addErrback( onFailure )<br>-           df.addCallbacks( onFinished, onFinished )<br>-            return df <br>-   def onFailure( reason ):<br>-             """Unable to log in!"""<br>-                print reason.getTraceback()<br>-          reactor.stop()<br>-       df.addCallbacks( onLogin, onFailure )<br>-        return df<br>+<br>+def main(channel='sip/20035@aci.on.ca', connectTo=('outgoing', 's', '1')):<br>+    f = manager.AMIFactory(sys.argv[1], sys.argv[2])<br>+    df = f.login()<br>+<br>+    def onLogin(protocol):<br>+        """On Login, attempt to originate the call"""<br>+        context, extension, priority = connectTo<br>+        df = protocol.originate(<br>+            channel,<br>+            context, extension, priority,<br>+        )<br>+<br>+        def onFinished(result):<br>+            df = protocol.logoff()<br>+<br>+            def onLogoff(result):<br>+                reactor.stop()<br>+            return df.addCallbacks(onLogoff, onLogoff)<br>+<br>+        def onFailure(reason):<br>+            print(reason.getTraceback())<br>+            return reason<br>+        df.addErrback(onFailure)<br>+        df.addCallbacks(onFinished, onFinished)<br>+        return df<br>+<br>+    def onFailure(reason):<br>+        """Unable to log in!"""<br>+        print(reason.getTraceback())<br>+        reactor.stop()<br>+    df.addCallbacks(onLogin, onFailure)<br>+    return df<br>+<br> <br> if __name__ == "__main__":<br>-       manager.log.setLevel( logging.DEBUG )<br>-        logging.basicConfig()<br>-        reactor.callWhenRunning( main )<br>-      reactor.run()<br>+    manager.log.setLevel(logging.DEBUG)<br>+    logging.basicConfig()<br>+    reactor.callWhenRunning(main)<br>+    reactor.run()<br>diff --git a/examples/connecttoivrapp.py b/examples/connecttoivrapp.py<br>index aeed61d..829ab66 100644<br>--- a/examples/connecttoivrapp.py<br>+++ b/examples/connecttoivrapp.py<br>@@ -3,35 +3,41 @@<br> This version of the script uses the utilapplication framework and is<br> pared down for presentation on a series of slides<br> """<br>-from starpy import manager<br> import utilapplication<br> from twisted.internet import reactor<br>-import sys, logging<br>+import logging<br>+<br> APPLICATION = utilapplication.UtilApplication()<br> <br>-def main( channel = 'sip/4167290048@testout', connectTo=('outgoing','s','1') ):<br>-    df = APPLICATION.amiSpecifier.login()<br>-        def onLogin( protocol ):<br>-             """We've logged into the manager, generate a call and log off"""<br>-           context, extension, priority = connectTo<br>-             df = protocol.originate(<br>-                     channel,<br>-                     context,extension,priority,<br>-          )<br>-            def onFinished( result ):<br>-                    return protocol.logoff()<br>-             df.addCallbacks( onFinished, onFinished )<br>-            return df <br>-   def onFailure( reason ):<br>-             print reason.getTraceback()<br>-  def onFinished( result ):<br>-            reactor.stop()<br>-       df.addCallbacks( <br>-            onLogin, onFailure <br>-  ).addCallbacks( onFinished, onFinished )<br>-     return df<br>+<br>+def main(channel='sip/4167290048@testout', connectTo=('outgoing', 's', '1')):<br>+    df = APPLICATION.amiSpecifier.login()<br>+<br>+    def onLogin(protocol):<br>+        """We've logged into the manager, generate a call and log off"""<br>+        context, extension, priority = connectTo<br>+        df = protocol.originate(<br>+            channel,<br>+            context, extension, priority,<br>+        )<br>+<br>+        def onFinished(result):<br>+            return protocol.logoff()<br>+        df.addCallbacks(onFinished, onFinished)<br>+        return df<br>+<br>+    def onFailure(reason):<br>+        print(reason.getTraceback())<br>+<br>+    def onFinished(result):<br>+        reactor.stop()<br>+    df.addCallbacks(<br>+        onLogin, onFailure<br>+    ).addCallbacks(onFinished, onFinished)<br>+    return df<br>+<br> <br> if __name__ == "__main__":<br>-  logging.basicConfig()<br>-        reactor.callWhenRunning( main )<br>-      reactor.run()<br>+    logging.basicConfig()<br>+    reactor.callWhenRunning(main)<br>+    reactor.run()<br>diff --git a/examples/fastagisetvariable.py b/examples/fastagisetvariable.py<br>index 44c3da7..3a4b774 100644<br>--- a/examples/fastagisetvariable.py<br>+++ b/examples/fastagisetvariable.py<br>@@ -3,26 +3,31 @@<br> from twisted.internet import reactor<br> from starpy import fastagi<br> import utilapplication<br>-import logging, time<br>+import logging<br> <br>-log = logging.getLogger( 'hellofastagi' )<br>+log = logging.getLogger('hellofastagi')<br> <br>-def testFunction( agi ):<br>-    """Demonstrate simplistic use of the AGI interface with sequence of actions"""<br>- log.debug( 'testFunction' )<br>-  def setX( ):<br>-         return agi.setVariable( 'this"toset', 'That"2set' )<br>-        def getX( result ):<br>-          return agi.getVariable( 'this"toset' )<br>-  def onX( value ):<br>-            print 'Retrieved value', value <br>-              reactor.stop()<br>-       return setX().addCallback( getX ).addCallbacks( onX, onX )<br>+<br>+def testFunction(agi):<br>+    """Demonstrate simple use of the AGI interface with sequence of actions"""<br>+    log.debug('testFunction')<br>+<br>+    def setX():<br>+        return agi.setVariable('this"toset', 'That"2set')<br>+<br>+    def getX(result):<br>+        return agi.getVariable('this"toset')<br>+<br>+    def onX(value):<br>+        print('Retrieved value', value)<br>+        reactor.stop()<br>+    return setX().addCallback(getX).addCallbacks(onX, onX)<br>+<br> <br> if __name__ == "__main__":<br>-        logging.basicConfig()<br>-        fastagi.log.setLevel( logging.DEBUG )<br>-        APPLICATION = utilapplication.UtilApplication()<br>-      APPLICATION.handleCallsFor( 's', testFunction )<br>-      APPLICATION.agiSpecifier.run( APPLICATION.dispatchIncomingCall )<br>-     reactor.run()<br>+    logging.basicConfig()<br>+    fastagi.log.setLevel(logging.DEBUG)<br>+    APPLICATION = utilapplication.UtilApplication()<br>+    APPLICATION.handleCallsFor('s', testFunction)<br>+    APPLICATION.agiSpecifier.run(APPLICATION.dispatchIncomingCall)<br>+    reactor.run()<br>diff --git a/examples/getvariable.py b/examples/getvariable.py<br>index 6929240..43a8e60 100644<br>--- a/examples/getvariable.py<br>+++ b/examples/getvariable.py<br>@@ -4,56 +4,63 @@<br> from twisted.internet import reactor<br> from starpy import fastagi<br> import utilapplication<br>-import logging, time, pprint<br>+import logging<br>+import pprint<br> <br>-log = logging.getLogger( 'hellofastagi' )<br>+log = logging.getLogger('hellofastagi')<br> <br>-def envVars( agi ):<br>-        """Print out channel variables for display"""<br>-  vars = [<br>-             x.split( ' -- ' )[0].strip() <br>-                for x in agi.getVariable.__doc__.splitlines()<br>-                if len(x.split( ' -- ' )) == 2<br>-       ]<br>-    for var in vars:<br>-             yield var <br> <br>-def printVar( result, agi, vars ):<br>-   """Print out the variables produced by envVars"""<br>-      def doPrint( result, var ):<br>-          print '%r -- %r'%( var, result )<br>-     def notAvailable( reason, var ):<br>-             print '%r -- UNDEFINED'%( var, )<br>-     try:<br>-         var = vars.next()<br>-    except StopIteration, err:<br>-           return None<br>-  else:<br>-                return agi.getVariable( var ).addCallback( doPrint, var ).addErrback(<br>-                        notAvailable, var,<br>-           ).addCallback(<br>-                       printVar, agi, vars,<br>-         )<br>-    <br>+def envVars(agi):<br>+    """Print out channel variables for display"""<br>+    vars = [<br>+        x.split(' -- ')[0].strip()<br>+        for x in agi.getVariable.__doc__.splitlines()<br>+        if len(x.split(' -- ')) == 2<br>+    ]<br>+    for var in vars:<br>+        yield var<br> <br>-def testFunction( agi ):<br>-   """Print out known AGI variables"""<br>-    log.debug( 'testFunction' )<br>-  print 'AGI Variables'<br>-        pprint.pprint( agi.variables )<br>-       print 'Channel Variables'<br>-    sequence = fastagi.InSequence()<br>-      sequence.append( printVar, None, agi, envVars(agi) )<br>- sequence.append( agi.finish )<br>-        def onFailure( reason ):<br>-             log.error( "Failure: %s", reason.getTraceback())<br>-           agi.finish()<br>- return sequence().addErrback( onFailure )<br>+<br>+def printVar(result, agi, vars):<br>+    """Print out the variables produced by envVars"""<br>+<br>+    def doPrint(result, var):<br>+        print('%r -- %r' % (var, result))<br>+<br>+    def notAvailable(reason, var):<br>+        print('%r -- UNDEFINED' % (var,))<br>+    try:<br>+        var = vars.next()<br>+    except StopIteration as err:<br>+        return None<br>+    else:<br>+        return agi.getVariable(var).addCallback(doPrint, var).addErrback(<br>+            notAvailable, var,<br>+        ).addCallback(<br>+            printVar, agi, vars,<br>+        )<br>+<br>+<br>+def testFunction(agi):<br>+    """Print out known AGI variables"""<br>+    log.debug('testFunction')<br>+    print('AGI Variables')<br>+    pprint.pprint(agi.variables)<br>+    print('Channel Variables')<br>+    sequence = fastagi.InSequence()<br>+    sequence.append(printVar, None, agi, envVars(agi))<br>+    sequence.append(agi.finish)<br>+<br>+    def onFailure(reason):<br>+        log.error("Failure: %s", reason.getTraceback())<br>+        agi.finish()<br>+    return sequence().addErrback(onFailure)<br>+<br> <br> if __name__ == "__main__":<br>-     logging.basicConfig()<br>-        #fastagi.log.setLevel( logging.DEBUG )<br>-       APPLICATION = utilapplication.UtilApplication()<br>-      APPLICATION.handleCallsFor( 's', testFunction )<br>-      APPLICATION.agiSpecifier.run( APPLICATION.dispatchIncomingCall )<br>-     reactor.run()<br>+    logging.basicConfig()<br>+    # fastagi.log.setLevel(logging.DEBUG)<br>+    APPLICATION = utilapplication.UtilApplication()<br>+    APPLICATION.handleCallsFor('s', testFunction)<br>+    APPLICATION.agiSpecifier.run(APPLICATION.dispatchIncomingCall)<br>+    reactor.run()<br>diff --git a/examples/hellofastagi.py b/examples/hellofastagi.py<br>index 1434dad..f491ca4 100644<br>--- a/examples/hellofastagi.py<br>+++ b/examples/hellofastagi.py<br>@@ -2,24 +2,30 @@<br> """Simple FastAGI server using starpy"""<br> from twisted.internet import reactor<br> from starpy import fastagi<br>-import logging, time<br>+import logging<br>+import time<br> <br>-log = logging.getLogger( 'hellofastagi' )<br> <br>-def testFunction( agi ):<br>- """Demonstrate simplistic use of the AGI interface with sequence of actions"""<br>- log.debug( 'testFunction' )<br>-  sequence = fastagi.InSequence()<br>-      sequence.append( agi.sayDateTime, time.time() )<br>-      sequence.append( agi.finish )<br>-        def onFailure( reason ):<br>-             log.error( "Failure: %s", reason.getTraceback())<br>-           agi.finish()<br>- return sequence().addErrback( onFailure )<br>+log = logging.getLogger('hellofastagi')<br>+<br>+<br>+def testFunction(agi):<br>+    """Demonstrate simple use of the AGI interface with sequence of actions"""<br>+    log.debug('testFunction')<br>+    sequence = fastagi.InSequence()<br>+    sequence.append(agi.sayDateTime, time.time())<br>+    sequence.append(agi.finish)<br>+<br>+    def onFailure(reason):<br>+        log.error("Failure: %s", reason.getTraceback())<br>+        agi.finish()<br>+    return sequence().addErrback(onFailure)<br>+<br> <br> if __name__ == "__main__":<br>-  logging.basicConfig()<br>-        fastagi.log.setLevel( logging.DEBUG )<br>-        f = fastagi.FastAGIFactory(testFunction)<br>-     reactor.listenTCP(4573, f, 50, '127.0.0.1') # only binding on local interface<br>-        reactor.run()<br>+    logging.basicConfig()<br>+    fastagi.log.setLevel(logging.DEBUG)<br>+    f = fastagi.FastAGIFactory(testFunction)<br>+    # only binding on local interface<br>+    reactor.listenTCP(4573, f, 50, '127.0.0.1')<br>+    reactor.run()<br>diff --git a/examples/hellofastagiapp.py b/examples/hellofastagiapp.py<br>index 51bc06c..6ed80be 100644<br>--- a/examples/hellofastagiapp.py<br>+++ b/examples/hellofastagiapp.py<br>@@ -7,25 +7,29 @@<br> from twisted.internet import reactor<br> from starpy import fastagi<br> import utilapplication<br>-import logging, time<br>+import logging<br>+import time<br> <br>-log = logging.getLogger( 'hellofastagi' )<br>+log = logging.getLogger('hellofastagi')<br> <br>-def testFunction( agi ):<br>-   """Demonstrate simplistic use of the AGI interface with sequence of actions"""<br>- log.debug( 'testFunction' )<br>-  sequence = fastagi.InSequence()<br>-      sequence.append( agi.sayDateTime, time.time() )<br>-      sequence.append( agi.finish )<br>-        def onFailure( reason ):<br>-             log.error( "Failure: %s", reason.getTraceback())<br>-           agi.finish()<br>- return sequence().addErrback( onFailure )<br>+<br>+def testFunction(agi):<br>+    """Demonstrate simple use of the AGI interface with sequence of actions"""<br>+    log.debug('testFunction')<br>+    sequence = fastagi.InSequence()<br>+    sequence.append(agi.sayDateTime, time.time())<br>+    sequence.append(agi.finish)<br>+<br>+    def onFailure(reason):<br>+        log.error("Failure: %s", reason.getTraceback())<br>+        agi.finish()<br>+    return sequence().addErrback(onFailure)<br>+<br> <br> if __name__ == "__main__":<br>-       logging.basicConfig()<br>-        fastagi.log.setLevel( logging.DEBUG )<br>-        APPLICATION = utilapplication.UtilApplication()<br>-      APPLICATION.handleCallsFor( 's', testFunction )<br>-      APPLICATION.agiSpecifier.run( APPLICATION.dispatchIncomingCall )<br>-     reactor.run()<br>+    logging.basicConfig()<br>+    fastagi.log.setLevel(logging.DEBUG)<br>+    APPLICATION = utilapplication.UtilApplication()<br>+    APPLICATION.handleCallsFor('s', testFunction)<br>+    APPLICATION.agiSpecifier.run(APPLICATION.dispatchIncomingCall)<br>+    reactor.run()<br>diff --git a/examples/menu.py b/examples/menu.py<br>index a847c2d..847e255 100644<br>--- a/examples/menu.py<br>+++ b/examples/menu.py<br>@@ -1,6 +1,6 @@<br> #<br> # StarPy -- Asterisk Protocols for Twisted<br>-# <br>+#<br> # Copyright (c) 2006, Michael C. Fletcher<br> #<br> # Michael C. Fletcher <mcfletch@vrplumber.com><br>@@ -29,34 +29,38 @@<br> XXX add the reject/accept menus to the CollectDigits (requires soundfiles<br> in standard locations on the server, complicates install)<br> """<br>-from twisted.application import service, internet<br>-from twisted.internet import reactor, defer<br>-from starpy import manager, fastagi, error<br>-import utilapplication<br>-import os, logging, pprint, time<br>+from twisted.internet import defer<br>+from starpy import error<br>+import os<br>+import sys<br>+import logging<br> from basicproperty import common, propertied, basic<br> <br> log = logging.getLogger('menu')<br> log.setLevel(logging.DEBUG)<br>+<br> <br> class Interaction(propertied.Propertied):<br>     """Base class for user-interaction operations"""<br>     ALL_DIGITS = '0123456789*#'<br>     timeout = common.FloatProperty(<br>         "timeout", """Duration to wait for response before repeating message""",<br>-        defaultValue = 5,<br>+        defaultValue=5,<br>     )<br>     maxRepetitions = common.IntegerProperty(<br>         "maxRepetitions", """Maximum number of times to play before failure""",<br>-        defaultValue = 5,<br>+        defaultValue=5,<br>     )<br>     onSuccess = basic.BasicProperty(<br>-        "onSuccess", """Optional callback for success with signature method( result, runner )""",<br>+        "onSuccess",<br>+        """Optional callback for success with signature method( result, runner )""",<br>     )<br>     onFailure = basic.BasicProperty(<br>-        "onFailure", """Optional callback for failure with signature method( result, runner )""",<br>+        "onFailure",<br>+        """Optional callback for failure with signature method( result, runner )""",<br>     )<br>     runnerClass = None<br>+<br>     def __call__(self, agi, *args, **named):<br>         """Initiate AGI-based interaction with the user"""<br>         return self.runnerClass(model=self, agi=agi)(*args, **named)<br>@@ -67,6 +71,7 @@<br>     agi = basic.BasicProperty(<br>         "agi", """The AGI instance we use to communicate with the user""",<br>     )<br>+<br>     def defaultFinalDF(prop, client):<br>         """Produce the default finalDF with onSuccess/onFailure support"""<br>         df = defer.Deferred()<br>@@ -80,13 +85,13 @@<br>         return df<br>     finalDF = basic.BasicProperty(<br>         "finalDF", """Final deferred we will callback/errback on success/failure""",<br>-        defaultFunction = defaultFinalDF,<br>+        defaultFunction=defaultFinalDF,<br>     )<br>     del defaultFinalDF<br> <br>     alreadyRepeated = common.IntegerProperty(<br>         "alreadyRepeated", """Number of times we've repeated the message...""",<br>-        defaultValue = 0,<br>+        defaultValue=0,<br>     )<br>     model = basic.BasicProperty(<br>         "model", """The data-model that we are presenting to the user (e.g. Menu)""",<br>@@ -94,7 +99,7 @@<br> <br>     def returnResult(self, result):<br>         """Return result of deferred to our original caller"""<br>-        log.debug('returnResult: %s %s', self.model,result)<br>+        log.debug('returnResult: %s %s', self.model, result)<br>         if not self.finalDF.called:<br>             self.finalDF.debug = True<br>             self.finalDF.callback(result)<br>@@ -117,20 +122,23 @@<br>         """Take set of prompt-compatible objects and produce a PromptRunner for them"""<br>         realPrompt = []<br>         for p in prompt:<br>-            if isinstance(p, (str, unicode)):<br>+            if isinstance(p, str):<br>+                p = AudioPrompt(p)<br>+            elif sys.version_info <= (3, 0) and isinstance(p, unicode):  # noqa<br>                 p = AudioPrompt(p)<br>             elif isinstance(p, int):<br>                 p = NumberPrompt(p)<br>             elif not isinstance(p, Prompt):<br>-                raise TypeError( """Unknown prompt element type on %r: %s"""%(<br>-                    p, p.__class__,<br>-                ))<br>+                raise TypeError(<br>+                    """Unknown prompt element type on %r: %s""" % (<br>+                        p, p.__class__, )<br>+                )<br>             realPrompt.append(p)<br>         return PromptRunner(<br>-            elements = realPrompt,<br>-            escapeDigits = self.escapeDigits,<br>-            agi = self.agi,<br>-            timeout = self.model.timeout,<br>+            elements=realPrompt,<br>+            escapeDigits=self.escapeDigits,<br>+            agi=self.agi,<br>+            timeout=self.model.timeout,<br>         )<br> <br> <br>@@ -151,7 +159,7 @@<br>             # easiest possibility, just read out the file...<br>             return self.agi.getData(<br>                 soundFile, timeout=self.model.timeout,<br>-                maxDigits = getattr(self.model, 'maxDigits', None),<br>+                maxDigits=getattr(self.model, 'maxDigits', None),<br>             ).addCallback(self.onReadDigits).addErrback(self.returnError)<br>         else:<br>             raise NotImplemented("""Haven't got non-soundfile menus working yet""")<br>@@ -166,8 +174,9 @@<br>                 return False, 'Too few digits'<br>         return True, None<br> <br>-    def onReadDigits(self, (digits,timeout)):<br>+    def onReadDigits(self, values):<br>         """Deal with succesful result from reading digits"""<br>+        (digits, timeout) = values<br>         log.info("""onReadDigits: %r, %s""", digits, timeout)<br>         valid, reason = self.validEntry(digits)<br>         if (not digits) and (not timeout):<br>@@ -183,7 +192,8 @@<br>                 pass<br>             self.alreadyRepeated += 1<br>             if self.alreadyRepeated >= self.model.maxRepetitions:<br>-                log.warn("""User did not complete digit-entry for %s, timing out""", self.model)<br>+                log.warn("""User did not complete digit-entry for %s, timing out""",<br>+                         self.model)<br>                 raise error.MenuTimeout(<br>                     self.model,<br>                     """User did not finish digit-entry in %s passes of collection""" % (<br>@@ -201,6 +211,7 @@<br>     expected = common.StringLocaleProperty(<br>         "expected", """The value expected/required from the user for this run""",<br>     )<br>+<br>     def __call__(self, expected, *args, **named):<br>         """Begin the AGI processing for the menu"""<br>         self.expected = expected<br>@@ -223,9 +234,10 @@<br>     """Audio-collection runner, records user audio to a file on the asterisk server"""<br>     escapeDigits = common.StringLocaleProperty(<br>         "escapeDigits", """Set of digits which escape from recording""",<br>-        defaultFunction = lambda prop, client: client.model.escapeDigits,<br>-        setDefaultOnGet = False,<br>+        defaultFunction=lambda prop, client: client.model.escapeDigits,<br>+        setDefaultOnGet=False,<br>     )<br>+<br>     def __call__(self, *args, **named):<br>         """Begin the AGI processing for the menu"""<br>         self.readPrompt()<br>@@ -252,7 +264,7 @@<br>         else:<br>             return self.collectAudio()<br> <br>-    def collectAudio( self ):<br>+    def collectAudio(self):<br>         """We're supposed to record audio from the user with our model's parameters"""<br>         # XXX use a temporary file for recording the audio, then move to final destination<br>         log.debug('collectAudio')<br>@@ -280,8 +292,8 @@<br>         digits, typeOfExit, endpos = result<br>         if typeOfExit in ('hangup', 'timeout'):<br>             # expected common-case for recording...<br>-            return self.returnResult((self,(digits,typeOfExit,endpos)))<br>-        elif typeOfExit =='dtmf':<br>+            return self.returnResult((self, (digits, typeOfExit, endpos)))<br>+        elif typeOfExit == 'dtmf':<br>             raise error.MenuExit(<br>                 self.model,<br>                 """User cancelled entry of audio""",<br>@@ -297,7 +309,7 @@<br>             """Failure collecting audio for CollectAudio instance %s: %s""",<br>             self.model, reason.getTraceback(),<br>         )<br>-        return reason # re-raise the error...<br>+        return reason  # re-raise the error...<br> <br>     def moveToFinal(self, result):<br>         """On succesful recording, move temporaryFile to final file"""<br>@@ -305,13 +317,12 @@<br>             'Moving recorded audio %r to final destination %r',<br>             self.model.temporaryFile, self.model.filename<br>         )<br>-        import os<br>         try:<br>             os.rename(<br>                 '%s.%s' % (self.model.temporaryFile, self.model.format),<br>                 '%s.%s' % (self.model.filename, self.model.format),<br>             )<br>-        except (OSError, IOError), err:<br>+        except (OSError, IOError) as err:<br>             log.error(<br>                 """Unable to move temporary recording file %r to target file %r: %s""",<br>                 self.model.temporaryFile, self.model.filename,<br>@@ -324,6 +335,7 @@<br> <br> class MenuRunner(Runner):<br>     """User's single interaction with a given menu"""<br>+<br>     def defaultEscapeDigits(prop, client):<br>         """Return the default escape digits for the given client"""<br>         if client.model.tellInvalid:<br>@@ -333,9 +345,9 @@<br>         return escapeDigits<br>     escapeDigits = common.StringLocaleProperty(<br>         "escapeDigits", """Set of digits which escape from prompts to choose option""",<br>-        defaultFunction = defaultEscapeDigits,<br>+        defaultFunction=defaultEscapeDigits,<br>     )<br>-    del defaultEscapeDigits # clean up namespace<br>+    del defaultEscapeDigits  # clean up namespace<br> <br>     def __call__(self, *args, **named):<br>         """Begin the AGI processing for the menu"""<br>@@ -353,7 +365,8 @@<br>         if not pressed:<br>             self.alreadyRepeated += 1<br>             if self.alreadyRepeated >= self.model.maxRepetitions:<br>-                log.warn("""User did not complete menu selection for %s, timing out""", self.model)<br>+                log.warn("""User did not complete menu selection for %s, timing out""",<br>+                         self.model)<br>                 if not self.finalDF.called:<br>                     raise error.MenuTimeout(<br>                         self.model,<br>@@ -374,15 +387,16 @@<br>                             self.returnResult, self.returnError<br>                         )<br>                     elif hasattr(option, 'onSuccess'):<br>-                        return defer.maybeDeferred(option.onSuccess, pressed, self).addCallbacks(<br>-                            self.returnResult, self.returnError<br>-                        )<br>+                        return defer.maybeDeferred(option.onSuccess, pressed, self) \<br>+                            .addCallbacks(self.returnResult, self.returnError)<br>                     else:<br>-                        return self.returnResult([(option,pressed),])<br>+                        return self.returnResult([(option, pressed), ])<br>             # but it wasn't anything we expected...<br>             if not self.model.tellInvalid:<br>                 raise error.MenuUnexpectedOption(<br>-                    self.model, """User somehow selected %r, which isn't a recognised option?""" % (pressed,),<br>+                    self.model,<br>+                    "User somehow selected %r, which isn't a recognised option?" % (<br>+                        pressed,),<br>                 )<br>             else:<br>                 return self.agi.getOption(<br>@@ -424,8 +438,9 @@<br>         "options", """Set of options the user may select""",<br>     )<br>     tellInvalid = common.IntegerProperty(<br>-        "tellInvalid", """Whether to tell the user that their selection is unrecognised""",<br>-        defaultValue = True,<br>+        "tellInvalid",<br>+        """Whether to tell the user that their selection is unrecognised""",<br>+        defaultValue=True,<br>     )<br>     runnerClass = MenuRunner<br> <br>@@ -442,11 +457,12 @@<br>     menu = basic.BasicProperty(<br>         "menu", """The sub-menu we are presenting to the user""",<br>     )<br>+<br>     def __call__(self, pressed, parent):<br>         """Get result from the sub-menu, add ourselves into the result"""<br>         def onResult(result):<br>             log.debug("""Child menu %s result: %s""", self.menu, result)<br>-            result.insert(0, (self,pressed))<br>+            result.insert(0, (self, pressed))<br>             return result<br> <br>         def onFailure(reason):<br>@@ -460,6 +476,7 @@<br> <br> class ExitOn(Option):<br>     """An option which exits from the current menu level"""<br>+<br>     def __call__(self, pressed, parent):<br>         """Raise a MenuExit error"""<br>         raise error.MenuExit(<br>@@ -477,18 +494,21 @@<br>     )<br>     readBack = common.BooleanProperty(<br>         "readBack", """Whether to read the entered value back to the user""",<br>-        defaultValue = False,<br>+        defaultValue=False,<br>     )<br>     minDigits = common.IntegerProperty(<br>-        "minDigits", """Minimum number of digits to collect (only restricted if specified)""",<br>+        "minDigits",<br>+        """Minimum number of digits to collect (only restricted if specified)""",<br>     )<br>     maxDigits = common.IntegerProperty(<br>-        "maxDigits", """Maximum number of digits to collect (only restricted if specified)""",<br>+        "maxDigits",<br>+        """Maximum number of digits to collect (only restricted if specified)""",<br>     )<br>     runnerClass = CollectDigitsRunner<br>     tellInvalid = common.IntegerProperty(<br>-        "tellInvalid", """Whether to tell the user that their selection is unrecognised""",<br>-        defaultValue = True,<br>+        "tellInvalid",<br>+        """Whether to tell the user that their selection is unrecognised""",<br>+        defaultValue=True,<br>     )<br> <br> <br>@@ -497,11 +517,11 @@<br>     runnerClass = CollectPasswordRunner<br>     escapeDigits = common.StringLocaleProperty(<br>         "escapeDigits", """Set of digits which escape from password entry""",<br>-        defaultValue = '',<br>+        defaultValue='',<br>     )<br>     soundFile = common.StringLocaleProperty(<br>         "soundFile", """File (name) for the pre-recorded blurb""",<br>-        defaultValue = 'vm-password',<br>+        defaultValue='vm-password',<br>     )<br> <br> <br>@@ -517,30 +537,31 @@<br>         "textPrompt", """Textual prompt describing the option""",<br>     )<br>     temporaryFile = common.StringLocaleProperty(<br>-        "temporaryFile", """Temporary file into which to record the audio before moving to filename""",<br>+        "temporaryFile",<br>+        """Temporary file into which to record the audio before moving to filename""",<br>     )<br>     filename = common.StringLocaleProperty(<br>         "filename", """Final filename into which to record the file...""",<br>     )<br>     deleteOnFail = common.BooleanProperty(<br>         "deleteOnFail", """Whether to delete failed attempts to record a file""",<br>-        defaultValue = True<br>+        defaultValue=True<br>     )<br>     escapeDigits = common.StringLocaleProperty(<br>         "escapeDigits", """Set of digits which escape from recording the file""",<br>-        defaultValue = '#*0123456789',<br>+        defaultValue='#*0123456789',<br>     )<br>     timeout = common.FloatProperty(<br>         "timeout", """Duration to wait for recording (maximum record time)""",<br>-        defaultValue = 60,<br>+        defaultValue=60,<br>     )<br>     silence = common.FloatProperty(<br>         "silence", """Duration to wait for recording (maximum record time)""",<br>-        defaultValue = 5,<br>+        defaultValue=5,<br>     )<br>     beep = common.BooleanProperty(<br>         "beep", """Whether to play a "beep" sound at beginning of recording""",<br>-        defaultValue = True,<br>+        defaultValue=True,<br>     )<br>     runnerClass = CollectAudioRunner<br> <br>@@ -560,6 +581,7 @@<br>     timeout = common.FloatProperty(<br>         "timeout", """Timeout on data-entry after completed reading""",<br>     )<br>+<br>     def __call__(self):<br>         """Return a deferred that chains all of the sub-prompts in order<br> <br>@@ -575,7 +597,7 @@<br>             return result<br>         try:<br>             element = self.elements[index]<br>-        except IndexError, err:<br>+        except IndexError as err:<br>             # okay, do a waitForDigit from timeout seconds...<br>             return self.agi.waitForDigit(self.timeout).addCallback(<br>                 self.processKey<br>@@ -592,7 +614,7 @@<br>             # getOption result...<br>             if result[1] == 0:<br>                 # failure during load of the file...<br>-                log.warn("""Apparent failure during load of audio file: %s""", self.value)<br>+                log.warn("Apparent failure during load of audio file: %s", self.value)<br>                 result = 0<br>             else:<br>                 result = result[0]<br>@@ -601,7 +623,7 @@<br>                     result = ord(result)<br>                 else:<br>                     result = 0<br>-        if result: # None or 0<br>+        if result:  # None or 0<br>             # User pressed a key during the reading...<br>             key = chr(result)<br>             if key in self.escapeDigits:<br>@@ -613,7 +635,7 @@<br>             # completed reading without any escape digits, continue reading<br>         return None<br> <br>-    def processLast(self,result):<br>+    def processLast(self, result):<br>         if result is None:<br>             result = ''<br>         return result<br>@@ -624,6 +646,7 @@<br>     value = basic.BasicProperty(<br>         "value", """Filename to be read to the user""",<br>     )<br>+<br>     def __init__(self, value, **named):<br>         named['value'] = value<br>         super(Prompt, self).__init__(**named)<br>@@ -648,6 +671,7 @@<br>     value = common.IntegerProperty(<br>         "value", """Integer numeral to read""",<br>     )<br>+<br>     def read(self, agi, escapeDigits):<br>         """Read the audio prompt to the user"""<br>         return agi.sayNumber(self.value, escapeDigits)<br>@@ -671,8 +695,9 @@<br>     """Prompt that reads a date/time as a date"""<br>     format = basic.BasicProperty(<br>         "format", """Format in which to read the date to the user""",<br>-        defaultValue = None<br>+        defaultValue=None<br>     )<br>+<br>     def read(self, agi, escapeDigits):<br>         """Read the audio prompt to the user"""<br>         return agi.sayDateTime(self.value, escapeDigits, format=self.format)<br>diff --git a/examples/menutest.py b/examples/menutest.py<br>index 55d6726..e7a1ab0 100644<br>--- a/examples/menutest.py<br>+++ b/examples/menutest.py<br>@@ -1,81 +1,80 @@<br> #! /usr/bin/env python<br> """Sample application to test the menuing utility classes"""<br>-from twisted.application import service, internet<br>-from twisted.internet import reactor, defer<br>-from starpy import manager, fastagi, error<br>+from twisted.internet import reactor<br>+from starpy import fastagi<br> import utilapplication<br> import menu<br>-import os, logging, pprint, time<br>+import logging<br> <br>-log = logging.getLogger( 'menutest' )<br>+log = logging.getLogger('menutest')<br> <br> mainMenu = menu.Menu(<br>-    prompt = '/home/mcfletch/starpydemo/soundfiles/menutest-toplevel',<br>-   #prompt = 'houston',<br>- textPrompt = '''Top level of the menu test example<br>-       <br>-     Pressing Star will exit this menu at any time.<br>-       Options zero and pound will exit with those options selected.<br>-        Option one will start a submenu.<br>-     Option two will start a digit-collecting sub-menu.<br>-   We'll tell you if you make an invalid selection here.''',<br>-    options = [<br>-          menu.Option( option='0' ),<br>-           menu.Option( option='#' ),<br>-           menu.ExitOn( option='*' ),<br>-           menu.SubMenu( <br>-                       option='1',<br>-                  menu = menu.Menu(<br>-                            prompt = '/home/mcfletch/starpydemo/soundfiles/menutest-secondlevel',<br>-                                #prompt = 'atlantic',<br>-                                textPrompt = '''A second-level menu in the menu test example<br>-                             <br>-                             Pressing Star will exit this menu at any time.<br>-                               Options zero and pound will exit the whole menu with those options selected.<br>-                         We won't tell you if you make an invalid selection here.<br>-                         ''',<br>-                             tellInvalid = False, # don't report incorrect selections<br>-                         options = [<br>-                                  menu.Option( option='0' ),<br>-                                   menu.Option( option='#' ),<br>-                                   menu.ExitOn( option='*' ),<br>-                           ],<br>-                   ),<br>-           ),<br>-           menu.SubMenu(<br>-                        option='2',<br>-                  menu = menu.CollectDigits(<br>-                           textPrompt = '''Digit collection example,<br>-                                Please enter three to 5 digits.<br>-                              ''',<br>-                             soundFile = '/home/mcfletch/starpydemo/soundfiles/menutest-digits',<br>-                          #soundFile = 'extension',<br>-                            maxDigits = 5,<br>-                               minDigits = 3,<br>-                       ),<br>-           ),<br>-   ],<br>+    prompt='/home/mcfletch/starpydemo/soundfiles/menutest-toplevel',<br>+    textPrompt='''Top level of the menu test example<br>+<br>+    Pressing Star will exit this menu at any time.<br>+    Options zero and pound will exit with those options selected.<br>+    Option one will start a submenu.<br>+    Option two will start a digit-collecting sub-menu.<br>+    We'll tell you if you make an invalid selection here.''',<br>+    options=[<br>+        menu.Option(option='0'),<br>+        menu.Option(option='#'),<br>+        menu.ExitOn(option='*'),<br>+        menu.SubMenu(<br>+            option='1',<br>+            menu=menu.Menu(<br>+                prompt='/home/mcfletch/starpydemo/soundfiles/menutest-secondlevel',<br>+                textPrompt='''A second-level menu in the menu test example<br>+<br>+                Pressing Star will exit this menu at any time. Options zero<br>+                and pound will exit the whole menu with those options selected.<br>+                We won't tell you if you make an invalid selection here.<br>+                ''',<br>+                tellInvalid=False,  # don't report incorrect selections<br>+                options=[<br>+                    menu.Option(option='0'),<br>+                    menu.Option(option='#'),<br>+                    menu.ExitOn(option='*'),<br>+                ],<br>+            ),<br>+        ),<br>+        menu.SubMenu(<br>+            option='2',<br>+            menu=menu.CollectDigits(<br>+                textPrompt='''Digit collection example,<br>+                Please enter three to 5 digits.<br>+                ''',<br>+                soundFile='/home/mcfletch/starpydemo/soundfiles/menutest-digits',<br>+                maxDigits=5,<br>+                minDigits=3,<br>+            ),<br>+        ),<br>+    ],<br> )<br> <br>-class Application( utilapplication.UtilApplication ):<br>-  """Application for the call duration callback mechanism"""<br>-     def onS( self, agi ):<br>-                """Incoming AGI connection to the "s" extension (start operation)"""<br>-         log.info( """New call tracker""" )<br>-             def onComplete( result ):<br>-                    log.info( """Final result: %r""", result )<br>-                     agi.finish()<br>-         return mainMenu( agi ).addCallbacks( onComplete, onComplete )<br>+<br>+class Application(utilapplication.UtilApplication):<br>+    """Application for the call duration callback mechanism"""<br>+<br>+    def onS(self, agi):<br>+        """Incoming AGI connection to the "s" extension (start operation)"""<br>+        log.info("""New call tracker""")<br>+<br>+        def onComplete(result):<br>+            log.info("""Final result: %r""", result)<br>+            agi.finish()<br>+        return mainMenu(agi).addCallbacks(onComplete, onComplete)<br>+<br> <br> APPLICATION = Application()<br> <br> if __name__ == "__main__":<br>-    logging.basicConfig()<br>-        log.setLevel( logging.DEBUG )<br>-        #manager.log.setLevel( logging.DEBUG )<br>-       fastagi.log.setLevel( logging.DEBUG )<br>-        menu.log.setLevel( logging.DEBUG )<br>-   APPLICATION.handleCallsFor( 's', APPLICATION.onS )<br>-   APPLICATION.agiSpecifier.run( APPLICATION.dispatchIncomingCall )<br>-     from twisted.internet import reactor<br>- reactor.run()<br>+    logging.basicConfig()<br>+    log.setLevel(logging.DEBUG)<br>+    # manager.log.setLevel(logging.DEBUG)<br>+    fastagi.log.setLevel(logging.DEBUG)<br>+    menu.log.setLevel(logging.DEBUG)<br>+    APPLICATION.handleCallsFor('s', APPLICATION.onS)<br>+    APPLICATION.agiSpecifier.run(APPLICATION.dispatchIncomingCall)<br>+    reactor.run()<br>diff --git a/examples/priexhaustion.py b/examples/priexhaustion.py<br>index 6f70cae..167f0cd 100644<br>--- a/examples/priexhaustion.py<br>+++ b/examples/priexhaustion.py<br>@@ -2,99 +2,104 @@<br> """Sample application to watch for PRI exhaustion<br> <br> This script watches for events on the AMI interface, tracking the identity of<br>-open channels in order to track how many channels are being used.  This would <br>-be used to send messages to an administrator when network capacity is being <br>+open channels in order to track how many channels are being used.  This would<br>+be used to send messages to an administrator when network capacity is being<br> approached.<br> <br>-Similarly, you could watch for spare capacity on the network and use that <br>+Similarly, you could watch for spare capacity on the network and use that<br> to decide whether to allow low-priority calls, such as peering framework or<br> free-world-dialup calls to go through.<br> """<br>-from twisted.application import service, internet<br>-from twisted.internet import reactor, defer<br>-from starpy import manager, fastagi<br>+from twisted.internet import reactor<br> import utilapplication<br>-import menu<br>-import os, logging, pprint, time<br>-from basicproperty import common, propertied, basic<br>+import logging<br>+from basicproperty import common, propertied<br> <br>-log = logging.getLogger( 'priexhaustion' )<br>-log.setLevel( logging.INFO )<br>+log = logging.getLogger('priexhaustion')<br>+log.setLevel(logging.INFO)<br> <br>-class ChannelTracker( propertied.Propertied ):<br>-        """Track open channels on the Asterisk server"""<br>-       channels = common.DictionaryProperty(<br>-                "channels", """Set of open channels on the system""",<br>-        )<br>-    thresholdCount = common.IntegerProperty(<br>-             "thresholdCount", """Storage of threshold below which we don't warn user""",<br>-             defaultValue = 20,<br>-   )<br>-    def main( self ):<br>-            """Main operation for the channel-tracking demo"""<br>-             amiDF = APPLICATION.amiSpecifier.login( <br>-             ).addCallback( self.onAMIConnect )<br>-           # XXX do something useful on failure to login...<br>-     def onAMIConnect( self, ami ):<br>-               """Register for AMI events"""<br>-          # XXX should do an initial query to populate channels...<br>-             # XXX should handle asterisk reboots (at the moment the AMI <br>-         # interface will just stop generating events), not a practical<br>-               # problem at the moment, but should have a periodic check to be sure<br>-         # the interface is still up, and if not, should close and restart<br>-            log.debug( 'onAMIConnect' )<br>-          ami.status().addCallback( self.onStatus, ami=ami )<br>-           ami.registerEvent( 'Hangup', self.onChannelHangup )<br>-          ami.registerEvent( 'Newchannel', self.onChannelNew )<br>- def interestingEvent( self, event, ami=None ):<br>-               """Decide whether this channel event is interesting <br>-          <br>-             Real-world application would want to take only Zap channels, or only<br>-         channels from a given context, or whatever other filter you want in <br>-         order to capture *just* the scarce resource (such as PRI lines).<br>-             <br>-             Keep in mind that an "interesting" event must show up as interesting <br>-              for *both* Newchannel and Hangup events or you will leak <br>-            references/channels or have unknown channels hanging up.<br>-             """<br>-           return True<br>-  def onStatus( self, events, ami=None ):<br>-              """Integrate the current status into our set of channels"""<br>-            log.debug( """Initial channel status retrieved""" )<br>-            for event in events:<br>-                 self.onChannelNew( ami, event )<br>-      def onChannelNew( self, ami, event ):<br>-                """Handle creation of a new channel"""<br>-         log.debug( """Start on channel %s""", event )<br>-          if self.interestingEvent( event, ami ):<br>-                      opening = not self.channels.has_key( event['uniqueid'] )<br>-                     self.channels[ event['uniqueid'] ] = event <br>-                  if opening:<br>-                          self.onChannelChange( ami, event, opening = opening )<br>-        def onChannelHangup( self, ami, event ):<br>-             """Handle hangup of an existing channel"""<br>-             if self.interestingEvent( event, ami ):<br>-                      try:<br>-                         del self.channels[ event['uniqueid']]<br>-                        except KeyError, err:<br>-                                log.warn( """Hangup on unknown channel %s""", event )<br>-                  else:<br>-                                log.debug( """Hangup on channel %s""", event )<br>-                 self.onChannelChange( ami, event, opening = False )<br>-  def onChannelChange( self, ami, event, opening=False ):<br>-              """Channel count has changed, do something useful like enforcing limits"""<br>-             if opening and len(self.channels) > self.thresholdCount:<br>-                  log.warn( """Current channel count: %s""", len(self.channels ) )<br>-               else:<br>-                        log.info( """Current channel count: %s""", len(self.channels ) )<br>+<br>+class ChannelTracker(propertied.Propertied):<br>+    """Track open channels on the Asterisk server"""<br>+    channels = common.DictionaryProperty(<br>+        "channels", """Set of open channels on the system""",<br>+    )<br>+    thresholdCount = common.IntegerProperty(<br>+        "thresholdCount", """Storage of threshold below which we don't warn user""",<br>+        defaultValue=20,<br>+    )<br>+<br>+    def main(self):<br>+        """Main operation for the channel-tracking demo"""<br>+        APPLICATION.amiSpecifier.login().addCallback(self.onAMIConnect)<br>+        # XXX do something useful on failure to login...<br>+<br>+    def onAMIConnect(self, ami):<br>+        """Register for AMI events"""<br>+        # XXX should do an initial query to populate channels...<br>+        # XXX should handle asterisk reboots (at the moment the AMI<br>+        # interface will just stop generating events), not a practical<br>+        # problem at the moment, but should have a periodic check to be sure<br>+        # the interface is still up, and if not, should close and restart<br>+        log.debug('onAMIConnect')<br>+        ami.status().addCallback(self.onStatus, ami=ami)<br>+        ami.registerEvent('Hangup', self.onChannelHangup)<br>+        ami.registerEvent('Newchannel', self.onChannelNew)<br>+<br>+    def interestingEvent(self, event, ami=None):<br>+        """Decide whether this channel event is interesting<br>+<br>+        Real-world application would want to take only Zap channels, or only<br>+        channels from a given context, or whatever other filter you want in<br>+        order to capture *just* the scarce resource (such as PRI lines).<br>+<br>+        Keep in mind that an "interesting" event must show up as interesting<br>+        for *both* Newchannel and Hangup events or you will leak<br>+        references/channels or have unknown channels hanging up.<br>+        """<br>+        return True<br>+<br>+    def onStatus(self, events, ami=None):<br>+        """Integrate the current status into our set of channels"""<br>+        log.debug("""Initial channel status retrieved""")<br>+        for event in events:<br>+            self.onChannelNew(ami, event)<br>+<br>+    def onChannelNew(self, ami, event):<br>+        """Handle creation of a new channel"""<br>+        log.debug("""Start on channel %s""", event)<br>+        if self.interestingEvent(event, ami):<br>+            opening = event['uniqueid'] not in self.channels<br>+            self.channels[event['uniqueid']] = event<br>+            if opening:<br>+                self.onChannelChange(ami, event, opening=opening)<br>+<br>+    def onChannelHangup(self, ami, event):<br>+        """Handle hangup of an existing channel"""<br>+        if self.interestingEvent(event, ami):<br>+            try:<br>+                del self.channels[event['uniqueid']]<br>+            except KeyError as err:<br>+                log.warn("""Hangup on unknown channel %s""", event)<br>+            else:<br>+                log.debug("""Hangup on channel %s""", event)<br>+            self.onChannelChange(ami, event, opening=False)<br>+<br>+    def onChannelChange(self, ami, event, opening=False):<br>+        """Channel count has changed, do something useful like enforcing limits"""<br>+        if opening and len(self.channels) > self.thresholdCount:<br>+            log.warn("""Current channel count: %s""", len(self.channels))<br>+        else:<br>+            log.info("""Current channel count: %s""", len(self.channels))<br>+<br> <br> APPLICATION = utilapplication.UtilApplication()<br> <br> if __name__ == "__main__":<br>-  logging.basicConfig()<br>-        #log.setLevel( logging.DEBUG )<br>-       #manager.log.setLevel( logging.DEBUG )<br>-       #fastagi.log.setLevel( logging.DEBUG )<br>-       tracker = ChannelTracker()<br>-   reactor.callWhenRunning( tracker.main )<br>-      reactor.run()<br>+    logging.basicConfig()<br>+    # log.setLevel(logging.DEBUG)<br>+    # manager.log.setLevel(logging.DEBUG)<br>+    # fastagi.log.setLevel(logging.DEBUG)<br>+    tracker = ChannelTracker()<br>+    reactor.callWhenRunning(tracker.main)<br>+    reactor.run()<br>diff --git a/examples/priexhaustionbare.py b/examples/priexhaustionbare.py<br>index 4e0290b..450bf02 100644<br>--- a/examples/priexhaustionbare.py<br>+++ b/examples/priexhaustionbare.py<br>@@ -1,64 +1,69 @@<br> #! /usr/bin/env python<br>-from twisted.application import service, internet<br>-from twisted.internet import reactor, defer<br>-from starpy import manager, fastagi<br>+from twisted.internet import reactor<br> import utilapplication<br>-import menu<br>-import os, logging, pprint, time<br>-from basicproperty import common, propertied, basic<br>+import logging<br>+from basicproperty import common, propertied<br> <br>-log = logging.getLogger( 'priexhaustion' )<br>-log.setLevel( logging.INFO )<br>+log = logging.getLogger('priexhaustion')<br>+log.setLevel(logging.INFO)<br> <br>-class ChannelTracker( propertied.Propertied ):<br>-    """Track open channels on the Asterisk server"""<br>-       channels = common.DictionaryProperty(<br>-                "channels", """Set of open channels on the system""",<br>-        )<br>-    thresholdCount = common.IntegerProperty(<br>-             "thresholdCount", """Storage of threshold below which we don't warn user""",<br>-             defaultValue = 20,<br>-   )<br>-    def main( self ):<br>-            """Main operation for the channel-tracking demo"""<br>-             amiDF = APPLICATION.amiSpecifier.login( <br>-             ).addCallback( self.onAMIConnect )<br>-   def onAMIConnect( self, ami ):<br>-               ami.status().addCallback( self.onStatus, ami=ami )<br>-           ami.registerEvent( 'Hangup', self.onChannelHangup )<br>-          ami.registerEvent( 'Newchannel', self.onChannelNew )<br>- def onStatus( self, events, ami=None ):<br>-              """Integrate the current status into our set of channels"""<br>-            log.debug( """Initial channel status retrieved""" )<br>-            for event in events:<br>-                 self.onChannelNew( ami, event )<br>-      def onChannelNew( self, ami, event ):<br>-                """Handle creation of a new channel"""<br>-         log.debug( """Start on channel %s""", event )<br>-          opening = not self.channels.has_key( event['uniqueid'] )<br>-             self.channels[ event['uniqueid'] ] = event <br>-          if opening:<br>-                  self.onChannelChange( ami, event, opening = opening )<br>-        def onChannelHangup( self, ami, event ):<br>-             """Handle hangup of an existing channel"""<br>-             try:<br>-                 del self.channels[ event['uniqueid']]<br>-                except KeyError, err:<br>-                        log.warn( """Hangup on unknown channel %s""", event )<br>-          else:<br>-                        log.debug( """Hangup on channel %s""", event )<br>-         self.onChannelChange( ami, event, opening = False )<br>-  def onChannelChange( self, ami, event, opening=False ):<br>-              """Channel count has changed, do something useful like enforcing limits"""<br>-             if opening and len(self.channels) > self.thresholdCount:<br>-                  log.warn( """Current channel count: %s""", len(self.channels ) )<br>-               else:<br>-                        log.info( """Current channel count: %s""", len(self.channels ) )<br>+<br>+class ChannelTracker(propertied.Propertied):<br>+    """Track open channels on the Asterisk server"""<br>+    channels = common.DictionaryProperty(<br>+        "channels", """Set of open channels on the system""",<br>+    )<br>+    thresholdCount = common.IntegerProperty(<br>+        "thresholdCount", """Storage of threshold below which we don't warn user""",<br>+        defaultValue=20,<br>+    )<br>+<br>+    def main(self):<br>+        """Main operation for the channel-tracking demo"""<br>+        APPLICATION.amiSpecifier.login().addCallback(self.onAMIConnect)<br>+<br>+    def onAMIConnect(self, ami):<br>+        ami.status().addCallback(self.onStatus, ami=ami)<br>+        ami.registerEvent('Hangup', self.onChannelHangup)<br>+        ami.registerEvent('Newchannel', self.onChannelNew)<br>+<br>+    def onStatus(self, events, ami=None):<br>+        """Integrate the current status into our set of channels"""<br>+        log.debug("""Initial channel status retrieved""")<br>+        for event in events:<br>+            self.onChannelNew(ami, event)<br>+<br>+    def onChannelNew(self, ami, event):<br>+        """Handle creation of a new channel"""<br>+        log.debug("""Start on channel %s""", event)<br>+        opening = event['uniqueid'] not in self.channels<br>+        self.channels[event['uniqueid']] = event<br>+        if opening:<br>+            self.onChannelChange(ami, event, opening=opening)<br>+<br>+    def onChannelHangup(self, ami, event):<br>+        """Handle hangup of an existing channel"""<br>+        try:<br>+            del self.channels[event['uniqueid']]<br>+        except KeyError as err:<br>+            log.warn("""Hangup on unknown channel %s""", event)<br>+        else:<br>+            log.debug("""Hangup on channel %s""", event)<br>+        self.onChannelChange(ami, event, opening=False)<br>+<br>+    def onChannelChange(self, ami, event, opening=False):<br>+        """Channel count has changed, do something useful like enforcing limits"""<br>+        if opening and len(self.channels) > self.thresholdCount:<br>+            log.warn("""Current channel count: %s""", len(self.channels))<br>+        else:<br>+            log.info("""Current channel count: %s""", len(self.channels))<br>+<br> <br> APPLICATION = utilapplication.UtilApplication()<br> <br>+<br> if __name__ == "__main__":<br>-        logging.basicConfig()<br>-        tracker = ChannelTracker()<br>-   reactor.callWhenRunning( tracker.main )<br>-      reactor.run()<br>+    logging.basicConfig()<br>+    tracker = ChannelTracker()<br>+    reactor.callWhenRunning(tracker.main)<br>+    reactor.run()<br>diff --git a/examples/readingdigits.py b/examples/readingdigits.py<br>index 093f1be..5f2895b 100644<br>--- a/examples/readingdigits.py<br>+++ b/examples/readingdigits.py<br>@@ -1,60 +1,70 @@<br> #! /usr/bin/env python<br> """Read digits from the user in various ways..."""<br>-from twisted.internet import reactor, defer<br>-from starpy import fastagi, error<br>-import logging, time<br>+from twisted.internet import reactor<br>+from starpy import fastagi<br>+import logging<br> <br>-log = logging.getLogger( 'hellofastagi' )<br>+log = logging.getLogger('hellofastagi')<br> <br>-class DialPlan( object ):<br>- """Stupid little application to report how many times it's been accessed"""<br>-        def __init__( self ):<br>-                self.count = 0<br>-       def __call__( self, agi ):<br>-           """Store the AGI instance for later usage, kick off our operations"""<br>-          self.agi = agi <br>-              return self.start()<br>-  def start( self ):<br>-           """Begin the dial-plan-like operations"""<br>-              return self.agi.answer().addCallbacks( self.onAnswered, self.answerFailure )<br>- def answerFailure( self, reason ):<br>-           """Deal with a failure to answer"""<br>-            log.warn( <br>-                   """Unable to answer channel %r: %s""", <br>-                        self.agi.variables['agi_channel'], reason.getTraceback(),<br>-            )<br>-            self.agi.finish()<br>-    def onAnswered( self, resultLine ):<br>-          """We've managed to answer the channel, yay!"""<br>-            self.count += 1<br>-              return self.agi.wait( 2.0 ).addCallback( self.onWaited )<br>-     def onWaited( self, result ):<br>-                """We've finished waiting, tell the user the number"""<br>-             return self.agi.sayNumber( self.count, '*' ).addErrback(<br>-                     self.onNumberFailed,<br>-         ).addCallbacks(<br>-                      self.onFinished, self.onFinished,<br>-            )<br>-    def onFinished( self, resultLine ):<br>-          """We said the number correctly, hang up on the user"""<br>-                return self.agi.finish()<br>-     def onNumberFailed( self, reason ):<br>-          """We were unable to read the number to the user"""<br>-            log.warn( <br>-                   """Unable to read number to user on channel %r: %s""",<br>-                 self.agi.variables['agi_channel'], reason.getTraceback(),<br>-            )<br>-            <br>-     def onHangupFailure( self, reason ):<br>-         """Failed trying to hang up"""<br>-         log.warn( <br>-                   """Unable to hang up channel %r: %s""", <br>-                       self.agi.variables['agi_channel'], reason.getTraceback(),<br>-            )<br>+<br>+class DialPlan(object):<br>+    """Stupid little application to report how many times it's been accessed"""<br>+<br>+    def __init__(self):<br>+        self.count = 0<br>+<br>+    def __call__(self, agi):<br>+        """Store the AGI instance for later usage, kick off our operations"""<br>+        self.agi = agi<br>+        return self.start()<br>+<br>+    def start(self):<br>+        """Begin the dial-plan-like operations"""<br>+        return self.agi.answer().addCallbacks(self.onAnswered, self.answerFailure)<br>+<br>+    def answerFailure(self, reason):<br>+        """Deal with a failure to answer"""<br>+        log.warn(<br>+            """Unable to answer channel %r: %s""",<br>+            self.agi.variables['agi_channel'], reason.getTraceback(),<br>+        )<br>+        self.agi.finish()<br>+<br>+    def onAnswered(self, resultLine):<br>+        """We've managed to answer the channel, yay!"""<br>+        self.count += 1<br>+        return self.agi.wait(2.0).addCallback(self.onWaited)<br>+<br>+    def onWaited(self, result):<br>+        """We've finished waiting, tell the user the number"""<br>+        return self.agi.sayNumber(self.count, '*').addErrback(<br>+            self.onNumberFailed,<br>+        ).addCallbacks(<br>+            self.onFinished, self.onFinished,<br>+        )<br>+<br>+    def onFinished(self, resultLine):<br>+        """We said the number correctly, hang up on the user"""<br>+        return self.agi.finish()<br>+<br>+    def onNumberFailed(self, reason):<br>+        """We were unable to read the number to the user"""<br>+        log.warn(<br>+            """Unable to read number to user on channel %r: %s""",<br>+            self.agi.variables['agi_channel'], reason.getTraceback(),<br>+        )<br>+<br>+    def onHangupFailure(self, reason):<br>+        """Failed trying to hang up"""<br>+        log.warn(<br>+            """Unable to hang up channel %r: %s""",<br>+            self.agi.variables['agi_channel'], reason.getTraceback(),<br>+        )<br>+<br> <br> if __name__ == "__main__":<br>-  logging.basicConfig()<br>-        fastagi.log.setLevel( logging.DEBUG )<br>-        f = fastagi.FastAGIFactory(DialPlan())<br>-       reactor.listenTCP(4573, f, 50, '127.0.0.1') # only binding on local interface<br>-        reactor.run()<br>+    logging.basicConfig()<br>+    fastagi.log.setLevel(logging.DEBUG)<br>+    f = fastagi.FastAGIFactory(DialPlan())<br>+    reactor.listenTCP(4573, f, 50, '127.0.0.1')  # only binding on local interface<br>+    reactor.run()<br>diff --git a/examples/timestamp.py b/examples/timestamp.py<br>index e66e832..1793d6b 100644<br>--- a/examples/timestamp.py<br>+++ b/examples/timestamp.py<br>@@ -2,44 +2,52 @@<br> """Provide a trivial date-and-time service"""<br> from twisted.internet import reactor<br> from starpy import fastagi<br>-import logging, time<br>+import logging<br>+import time<br> <br>-log = logging.getLogger( 'dateandtime' )<br>+log = logging.getLogger('dateandtime')<br> <br>-def testFunction( agi ):<br>-       """Give time for some time a bit in the future"""<br>-      log.debug( 'testFunction' )<br>-  df = agi.streamFile( 'at-tone-time-exactly' )<br>-        def onFailed( reason ):<br>-              log.error( "Failure: %s", reason.getTraceback())<br>-           return None<br>-  def cleanup( result ):<br>-               agi.finish()<br>-         return result<br>-        def onSaid( resultLine ):<br>-            """Having introduced, actually read the time"""<br>-                t = time.time()<br>-              t2 = t+20.0<br>-          df = agi.sayDateTime( t2, format='HM' )<br>-              def onDateFinished( resultLine ):<br>-                    # now need to sleep until .5 seconds before the time <br>-                        df = agi.wait( t2-.5-time.time() )<br>-                   def onDoBeep( result ):<br>-                              df = agi.streamFile( 'beep' )<br>-                                return df<br>-                    return df.addCallback( onDoBeep )<br>-            return df.addCallback( onDateFinished )<br>-      return df.addCallback( <br>-              onSaid <br>-      ).addErrback( <br>-               onFailed <br>-    ).addCallbacks(<br>-              cleanup, cleanup,<br>-    )<br>+<br>+def testFunction(agi):<br>+    """Give time for some time a bit in the future"""<br>+    log.debug('testFunction')<br>+    df = agi.streamFile('at-tone-time-exactly')<br>+<br>+    def onFailed(reason):<br>+        log.error("Failure: %s", reason.getTraceback())<br>+        return None<br>+<br>+    def cleanup(result):<br>+        agi.finish()<br>+        return result<br>+<br>+    def onSaid(resultLine):<br>+        """Having introduced, actually read the time"""<br>+        t = time.time()<br>+        t2 = t+20.0<br>+        df = agi.sayDateTime(t2, format='HM')<br>+<br>+        def onDateFinished(resultLine):<br>+            # now need to sleep until .5 seconds before the time<br>+            df = agi.wait(t2-.5-time.time())<br>+<br>+            def onDoBeep(result):<br>+                df = agi.streamFile('beep')<br>+                return df<br>+            return df.addCallback(onDoBeep)<br>+        return df.addCallback(onDateFinished)<br>+    return df.addCallback(<br>+        onSaid<br>+    ).addErrback(<br>+        onFailed<br>+    ).addCallbacks(<br>+        cleanup, cleanup,<br>+    )<br>+<br> <br> if __name__ == "__main__":<br>-       logging.basicConfig()<br>-        fastagi.log.setLevel( logging.INFO )<br>- f = fastagi.FastAGIFactory(testFunction)<br>-     reactor.listenTCP(4574, f, 50, '127.0.0.1') # only binding on local interface<br>-        reactor.run()<br>+    logging.basicConfig()<br>+    fastagi.log.setLevel(logging.INFO)<br>+    f = fastagi.FastAGIFactory(testFunction)<br>+    reactor.listenTCP(4574, f, 50, '127.0.0.1')  # only binding on local interface<br>+    reactor.run()<br>diff --git a/examples/timestampapp.py b/examples/timestampapp.py<br>index 0585155..dba94b6 100644<br>--- a/examples/timestampapp.py<br>+++ b/examples/timestampapp.py<br>@@ -3,46 +3,55 @@<br> from twisted.internet import reactor<br> from starpy import fastagi<br> import utilapplication<br>-import logging, time<br>+import logging<br>+import time<br> <br>-log = logging.getLogger( 'dateandtime' )<br>+log = logging.getLogger('dateandtime')<br> <br>-def testFunction( agi ):<br>-       """Give time for some time a bit in the future"""<br>-      log.debug( 'testFunction' )<br>-  df = agi.streamFile( 'at-tone-time-exactly' )<br>-        def onFailed( reason ):<br>-              log.error( "Failure: %s", reason.getTraceback())<br>-           return None<br>-  def cleanup( result ):<br>-               agi.finish()<br>-         return result<br>-        def onSaid( resultLine ):<br>-            """Having introduced, actually read the time"""<br>-                t = time.time()<br>-              t2 = t+7.0<br>-           df = agi.sayDateTime( t2, format='HMS' )<br>-             def onDateFinished( resultLine ):<br>-                    # now need to sleep until .05 seconds before the time <br>-                       df = agi.wait( t2-.05-time.time() )<br>-                  def onDoBeep( result ):<br>-                              df = agi.streamFile( 'beep' )<br>-                                return df<br>-                    def waitTwo( result ):<br>-                               return agi.streamFile( 'thank-you-for-calling' )<br>-                     return df.addCallback( onDoBeep ).addCallback( waitTwo )<br>-             return df.addCallback( onDateFinished )<br>-      return df.addCallback( <br>-              onSaid <br>-      ).addErrback( <br>-               onFailed <br>-    ).addCallbacks(<br>-              cleanup, cleanup,<br>-    )<br>+<br>+def testFunction(agi):<br>+    """Give time for some time a bit in the future"""<br>+    log.debug('testFunction')<br>+    df = agi.streamFile('at-tone-time-exactly')<br>+<br>+    def onFailed(reason):<br>+        log.error("Failure: %s", reason.getTraceback())<br>+        return None<br>+<br>+    def cleanup(result):<br>+        agi.finish()<br>+        return result<br>+<br>+    def onSaid(resultLine):<br>+        """Having introduced, actually read the time"""<br>+        t = time.time()<br>+        t2 = t+7.0<br>+        df = agi.sayDateTime(t2, format='HMS')<br>+<br>+        def onDateFinished(resultLine):<br>+            # now need to sleep until .05 seconds before the time<br>+            df = agi.wait(t2-.05-time.time())<br>+<br>+            def onDoBeep(result):<br>+                df = agi.streamFile('beep')<br>+                return df<br>+<br>+            def waitTwo(result):<br>+                return agi.streamFile('thank-you-for-calling')<br>+            return df.addCallback(onDoBeep).addCallback(waitTwo)<br>+        return df.addCallback(onDateFinished)<br>+    return df.addCallback(<br>+        onSaid<br>+    ).addErrback(<br>+        onFailed<br>+    ).addCallbacks(<br>+        cleanup, cleanup,<br>+    )<br>+<br> <br> if __name__ == "__main__":<br>- logging.basicConfig()<br>-        fastagi.log.setLevel( logging.INFO )<br>- APPLICATION = utilapplication.UtilApplication()<br>-      reactor.callWhenRunning( APPLICATION.agiSpecifier.run, testFunction )<br>-        reactor.run()<br>+    logging.basicConfig()<br>+    fastagi.log.setLevel(logging.INFO)<br>+    APPLICATION = utilapplication.UtilApplication()<br>+    reactor.callWhenRunning(APPLICATION.agiSpecifier.run, testFunction)<br>+    reactor.run()<br>diff --git a/examples/utilapplication.py b/examples/utilapplication.py<br>index 0e51901..e7fc6ed 100644<br>--- a/examples/utilapplication.py<br>+++ b/examples/utilapplication.py<br>@@ -1,6 +1,6 @@<br> #<br> # StarPy -- Asterisk Protocols for Twisted<br>-# <br>+#<br> # Copyright (c) 2006, Michael C. Fletcher<br> #<br> # Michael C. Fletcher <mcfletch@vrplumber.com><br>@@ -15,15 +15,17 @@<br> # details.<br> <br> """Class providing utility applications with common support code"""<br>-from basicproperty import common, propertied, basic, weak<br>+from basicproperty import common, propertied, basic<br> from ConfigParser import ConfigParser<br> from starpy import fastagi, manager<br> from twisted.internet import defer, reactor<br>-import logging,os<br>+import logging<br>+import os<br> <br>-log = logging.getLogger( 'app' )<br>+log = logging.getLogger('app')<br> <br>-class UtilApplication( propertied.Propertied ):<br>+<br>+class UtilApplication(propertied.Propertied):<br>     """Utility class providing simple application-level operations<br> <br>     FastAGI entry points are waitForCallOn and handleCallsFor, which allow<br>@@ -32,82 +34,93 @@<br>     (as specified in self.configFiles).<br>     """<br>     amiSpecifier = basic.BasicProperty(<br>-        "amiSpecifier", """AMI connection specifier for the application see AMISpecifier""",<br>-        defaultFunction = lambda prop,client: AMISpecifier()<br>+        "amiSpecifier",<br>+        """AMI connection specifier for the application see AMISpecifier""",<br>+        defaultFunction=lambda prop, client: AMISpecifier()<br>     )<br>     agiSpecifier = basic.BasicProperty(<br>-        "agiSpecifier", """FastAGI server specifier for the application see AGISpecifier""",<br>-        defaultFunction = lambda prop,client: AGISpecifier()<br>+        "agiSpecifier",<br>+        """FastAGI server specifier for the application see AGISpecifier""",<br>+        defaultFunction=lambda prop, client: AGISpecifier()<br>     )<br>     extensionWaiters = common.DictionaryProperty(<br>         "extensionWaiters", """Set of deferreds waiting for incoming extensions""",<br>     )<br>     extensionHandlers = common.DictionaryProperty(<br>-        "extensionHandlers", """Set of permanant callbacks waiting for incoming extensions""",<br>+        "extensionHandlers",<br>+        """Set of permanant callbacks waiting for incoming extensions""",<br>     )<br>-    configFiles = configFiles=('starpy.conf','~/.starpy.conf')<br>-    def __init__( self ):<br>+    configFiles = ('starpy.conf', '~/.starpy.conf')<br>+<br>+    def __init__(self):<br>         """Initialise the application from options in configFile"""<br>         self.loadConfigurations()<br>-    def loadConfigurations( self ):<br>-        parser = self._loadConfigFiles( self.configFiles )<br>-        self._copyPropertiesFrom( parser, 'AMI', self.amiSpecifier )<br>-        self._copyPropertiesFrom( parser, 'FastAGI', self.agiSpecifier )<br>+<br>+    def loadConfigurations(self):<br>+        parser = self._loadConfigFiles(self.configFiles)<br>+        self._copyPropertiesFrom(parser, 'AMI', self.amiSpecifier)<br>+        self._copyPropertiesFrom(parser, 'FastAGI', self.agiSpecifier)<br>         return parser<br>-    def _loadConfigFiles( self, configFiles ):<br>+<br>+    def _loadConfigFiles(self, configFiles):<br>         """Load options from configuration files given (if present)"""<br>-        parser = ConfigParser( )<br>+        parser = ConfigParser()<br>         filenames = [<br>-            os.path.abspath( os.path.expandvars( os.path.expanduser( file ) ))<br>+            os.path.abspath(os.path.expandvars(os.path.expanduser(file)))<br>             for file in configFiles<br>         ]<br>-        log.info( "Possible configuration files:\n\t%s", "\n\t".join(filenames) or None)<br>+        log.info("Possible configuration files:\n\t%s", "\n\t".join(filenames) or None)<br>         filenames = [<br>             file for file in filenames<br>             if os.path.isfile(file)<br>         ]<br>-        log.info( "Actual configuration files:\n\t%s", "\n\t".join(filenames) or None)<br>-        parser.read( filenames )<br>+        log.info("Actual configuration files:\n\t%s", "\n\t".join(filenames) or None)<br>+        parser.read(filenames)<br>         return parser<br>-    def _copyPropertiesFrom( self, parser, section, client, properties=None ):<br>+<br>+    def _copyPropertiesFrom(self, parser, section, client, properties=None):<br>         """Copy properties from the config-parser's given section into client"""<br>         if properties is None:<br>             properties = client.getProperties()<br>         for property in properties:<br>-            if parser.has_option( section, property.name ):<br>+            if parser.has_option(section, property.name):<br>                 try:<br>-                    value = parser.get( section, property.name )<br>-                    setattr( client, property.name, value )<br>-                except (TypeError,ValueError,AttributeError,NameError), err:<br>-                    log( """Unable to set property %r of %r to config-file value %r: %s"""%(<br>-                        property.name, client, parser.get( section, property.name, 1), err,<br>+                    value = parser.get(section, property.name)<br>+                    setattr(client, property.name, value)<br>+                except (TypeError, ValueError, AttributeError, NameError) as err:<br>+                    log('Unable to set property %r of %r to config-file value %r: %s' % (<br>+                        property.name, client, parser.get(section, property.name, 1), err,<br>                     ))<br>         return client<br>-    def dispatchIncomingCall( self, agi ):<br>+<br>+    def dispatchIncomingCall(self, agi):<br>         """Handle an incoming call (dispatch to the appropriate registered handler)"""<br>         extension = agi.variables['agi_extension']<br>-        log.info( """AGI connection with extension: %r""",  extension )<br>+        log.info("""AGI connection with extension: %r""", extension)<br>         try:<br>-            df = self.extensionWaiters.pop( extension )<br>-        except KeyError, err:<br>+            df = self.extensionWaiters.pop(extension)<br>+        except KeyError as err:<br>             try:<br>-                callback = self.extensionHandlers[ extension ]<br>-            except KeyError, err:<br>+                callback = self.extensionHandlers[extension]<br>+            except KeyError as err:<br>                 try:<br>-                    callback = self.extensionHandlers[ None ]<br>-                except KeyError, err:<br>-                    log.warn( """Unexpected connection to extension %r: %s""", extension, agi.variables )<br>+                    callback = self.extensionHandlers[None]<br>+                except KeyError as err:<br>+                    log.warn("""Unexpected connection to extension %r: %s""",<br>+                             extension, agi.variables)<br>                     agi.finish()<br>                     return<br>             try:<br>-                return callback( agi )<br>-            except Exception, err:<br>-                log.error( """Failure during callback %s for agi %s: %s""", callback, agi.variables, err )<br>+                return callback(agi)<br>+            except Exception as err:<br>+                log.error("""Failure during callback %s for agi %s: %s""",<br>+                          callback, agi.variables, err)<br>                 # XXX return a -1 here<br>         else:<br>             if not df.called:<br>-                df.callback( agi )<br>-    def waitForCallOn( self, extension, timeout=15 ):<br>+                df.callback(agi)<br>+<br>+    def waitForCallOn(self, extension, timeout=15):<br>         """Wait for an AGI call on extension given<br> <br>         extension -- string extension for which to wait<br>@@ -122,17 +135,19 @@<br>         returns deferred returning connected FastAGIProtocol or an error<br>         """<br>         extension = str(extension)<br>-        log.info( 'Waiting for extension %r for %s seconds', extension, timeout )<br>-        df = defer.Deferred( )<br>-        self.extensionWaiters[ extension ] = df<br>-        def onTimeout( ):<br>+        log.info('Waiting for extension %r for %s seconds', extension, timeout)<br>+        df = defer.Deferred()<br>+        self.extensionWaiters[extension] = df<br>+<br>+        def onTimeout():<br>             if not df.called:<br>-                df.errback( defer.TimeoutError(<br>-                    """Timeout waiting for call on extension: %r"""%(extension,)<br>+                df.errback(defer.TimeoutError(<br>+                    """Timeout waiting for call on extension: %r""" % (extension,)<br>                 ))<br>-        reactor.callLater( timeout, onTimeout )<br>+        reactor.callLater(timeout, onTimeout)<br>         return df<br>-    def handleCallsFor( self, extension, callback ):<br>+<br>+    def handleCallsFor(self, extension, callback):<br>         """Register permanant handler for given extension<br> <br>         extension -- string extension for which to wait or None to define<br>@@ -150,9 +165,10 @@<br>         """<br>         if extension is not None:<br>             extension = str(extension)<br>-        self.extensionHandlers[ extension ] = callback<br>+        self.extensionHandlers[extension] = callback<br> <br>-class AMISpecifier( propertied.Propertied ):<br>+<br>+class AMISpecifier(propertied.Propertied):<br>     """Manager interface setup/specifier"""<br>     username = common.StringLocaleProperty(<br>         "username", """Login username for the manager interface""",<br>@@ -163,36 +179,39 @@<br>     password = secret<br>     server = common.StringLocaleProperty(<br>         "server", """Server IP address to which to connect""",<br>-        defaultValue = '127.0.0.1',<br>+        defaultValue='127.0.0.1',<br>     )<br>     port = common.IntegerProperty(<br>         "port", """Server IP port to which to connect""",<br>-        defaultValue = 5038,<br>+        defaultValue=5038,<br>     )<br>     timeout = common.FloatProperty(<br>         "timeout", """Timeout in seconds for an AMI connection timeout""",<br>-        defaultValue = 5.0,<br>+        defaultValue=5.0,<br>     )<br>-    def login( self ):<br>+<br>+    def login(self):<br>         """Login to the specified manager via the AMI"""<br>         theManager = manager.AMIFactory(self.username, self.secret)<br>         return theManager.login(self.server, self.port, timeout=self.timeout)<br> <br>-class AGISpecifier( propertied.Propertied ):<br>+<br>+class AGISpecifier(propertied.Propertied):<br>     """Specifier of where we send the user to connect to our AGI"""<br>     port = common.IntegerProperty(<br>         "port", """IP port on which to listen""",<br>-        defaultValue = 4573,<br>+        defaultValue=4573,<br>     )<br>     interface = common.StringLocaleProperty(<br>         "interface", """IP interface on which to listen (local only by default)""",<br>-        defaultValue = '127.0.0.1',<br>+        defaultValue='127.0.0.1',<br>     )<br>     context = common.StringLocaleProperty(<br>         "context", """Asterisk context to which to connect incoming calls""",<br>-        defaultValue = 'survey',<br>+        defaultValue='survey',<br>     )<br>-    def run( self, mainFunction ):<br>+<br>+    def run(self, mainFunction):<br>         """Start up the AGI server with the given mainFunction"""<br>         f = fastagi.FastAGIFactory(mainFunction)<br>         return reactor.listenTCP(self.port, f, 50, self.interface)<br>diff --git a/starpy/fastagi.py b/starpy/fastagi.py<br>index 7e26284..85de054 100644<br>--- a/starpy/fastagi.py<br>+++ b/starpy/fastagi.py<br>@@ -28,7 +28,6 @@<br> from twisted.internet import protocol, reactor, defer<br> from twisted.internet import error as tw_error<br> from twisted.protocols import basic<br>-import socket<br> import logging<br> import time<br> from starpy import error<br>@@ -236,9 +235,10 @@<br>                 if endpos == skipMS:<br>                     # "likely" an error according to the wiki,<br>                     # we'll raise an error...<br>-                    raise error.AGICommandFailure(FAILURE_CODE,<br>-                                "End position %s == original position, "<br>-                                "result code %s" % (endpos, digit))<br>+                    raise error.AGICommandFailure(<br>+                        FAILURE_CODE,<br>+                        "End position %s == original position, "<br>+                        "result code %s" % (endpos, digit))<br>                 return digit, endpos<br>         raise ValueError("Unexpected result on streaming completion: %r" %<br>                          resultLine)<br>@@ -267,8 +267,8 @@<br>         sequence = InSequence()<br>         sequence.append(self.setContext, self.variables['agi_context'])<br>         sequence.append(self.setExtension, self.variables['agi_extension'])<br>-        sequence.append(self.setPriority, int(self.variables['agi_priority'])<br>-                                              + difference)<br>+        sequence.append(self.setPriority,<br>+                        int(self.variables['agi_priority']) + difference)<br>         sequence.append(self.finish)<br>         return sequence()<br> <br>@@ -326,7 +326,7 @@<br>         """<br>         parts = resultLine.split(' ', 1)<br>         result = int(parts[0])<br>-        endpos = None # Default if endpos isn't specified<br>+        endpos = None  # Default if endpos isn't specified<br>         if len(parts) == 2:<br>             endposStuff = parts[1].strip()<br>             if endposStuff.startswith('endpos='):<br>@@ -379,7 +379,7 @@<br>         """<br>         command = 'DATABASE DELTREE "%s"' % (family,)<br>         if keyTree:<br>-            command += ' "%s"' % (keytree,)<br>+            command += ' "%s"' % (keyTree,)<br>         return self.sendCommand(command).addCallback(<br>             self.checkFailure, failure='0',<br>         ).addCallback(self.resultAsInt)<br>@@ -629,8 +629,7 @@<br> <br>     def recordFile(<br>             self, filename, format, escapeDigits, timeout=-1,<br>-            offsetSamples=None, beep=True, silence=None,<br>-        ):<br>+            offsetSamples=None, beep=True, silence=None,):<br>         """Record channel to given filename until escapeDigits or silence<br> <br>         filename -- filename on the server to which to save<br>diff --git a/starpy/manager.py b/starpy/manager.py<br>index 6876f74..7adac62 100644<br>--- a/starpy/manager.py<br>+++ b/starpy/manager.py<br>@@ -46,11 +46,13 @@<br>     """A subclass of defer.Deferred that adds a registerError method<br>     to handle function callback when an Error response happens"""<br>     _errorRespCallback = None<br>-    def registerError(self, function ):<br>+<br>+    def registerError(self, function):<br>         """Add function for Error response callback"""<br>         self._errorRespCallback = function<br>         log.debug('Registering function %s to handle Error response'<br>                   % (function))<br>+<br> <br> class AMIProtocol(basic.LineOnlyReceiver):<br>     """Protocol for the interfacing with the Asterisk Manager Interface (AMI)<br>@@ -239,10 +241,10 @@<br>             if line:<br>                 if line.endswith(self.END_DATA):<br>                     # multi-line command results...<br>-                    message.setdefault(' ', []).extend([<br>-                        l for l in line.split('\n')<br>-                                if (l and l != self.END_DATA)<br>-                    ])<br>+                    message.setdefault(' ', []).extend(<br>+                        [l for l in line.split('\n')<br>+                            if (l and l != self.END_DATA)]<br>+                    )<br>                 else:<br>                     # regular line...<br>                     if line.startswith(self.VERSION_PREFIX):<br>@@ -319,8 +321,9 @@<br> <br>     def checkErrorResponse(self, result, actionid, df):<br>         """Check for error response and callback"""<br>-        self.cleanup( result, actionid)<br>-        if isinstance(result, dict) and result.get('response') == 'Error' and df._errorRespCallback:<br>+        self.cleanup(result, actionid)<br>+        if isinstance(result, dict) and result.get('response') == 'Error' \<br>+                and df._errorRespCallback:<br>             df._errorRespCallback(result)<br>         return result<br> <br>@@ -398,7 +401,7 @@<br>             raise error.AMICommandFailure(message)<br>         return message<br> <br>-    ## End-user API<br>+    # End-user API<br>     def absoluteTimeout(self, channel, timeout):<br>         """Set timeout value for the given channel (in seconds)"""<br>         message = {<br>@@ -461,10 +464,9 @@<br> <br>     def action(self, action, **action_args):<br>         """Sends an arbitrary action to the AMI"""<br>-        #action_args will be ar least an empty dict so we build the message from it.<br>+        # action_args will be at least an empty dict so we build the message from it.<br>         action_args['action'] = action<br>         return self.sendDeferred(action_args).addCallback(self.errorUnlessResponse)<br>-<br> <br>     def dbDel(self, family, key):<br>         """Delete key value in the AstDB database"""<br>@@ -493,7 +495,8 @@<br>             value = event['val']<br>             self.deregisterEvent("DBGetResponse", extractValue)<br>             return df.callback(value)<br>-        def errorResponse( message ):<br>+<br>+        def errorResponse(message):<br>             self.deregisterEvent("DBGetResponse", extractValue)<br>             return df.callback(None)<br>         message = {<br>@@ -612,14 +615,16 @@<br>     def loginChallengeResponse(self):<br>         """Log into the AMI interface with challenge-response.<br> <br>-        Follows the same approach as self.login() using factory.username and factory.secret.<br>-        Also done automatically on connection: will be called instead of self.login() if<br>-        factory.plaintext_login is False: see AMIFactory constructor.<br>+        Follows the same approach as self.login() using factory.username and<br>+        factory.secret.  Also done automatically on connection: will be called<br>+        instead of self.login() if factory.plaintext_login is False: see<br>+        AMIFactory constructor.<br>         """<br>         def sendResponse(challenge):<br>-            if not type(challenge) is dict or not 'challenge' in challenge:<br>+            if not type(challenge) is dict or 'challenge' not in challenge:<br>                 raise error.AMICommandFailure(challenge)<br>-            key_value = md5('%s%s' % (challenge['challenge'], self.factory.secret)).hexdigest()<br>+            key_value = md5('%s%s' % (challenge['challenge'], self.factory.secret)) \<br>+                .hexdigest()<br>             return self.sendDeferred({<br>                 'action': 'Login',<br>                 'authtype': 'MD5',<br>@@ -712,8 +717,7 @@<br>             self, channel, context=None, exten=None, priority=None,<br>             timeout=None, callerid=None, account=None, application=None,<br>             data=None, variable={}, async=False, channelid=None,<br>-                       otherchannelid=None<br>-        ):<br>+            otherchannelid=None):<br>         """Originate call to connect channel to given context/exten/priority<br> <br>         channel -- the outgoing channel to which will be dialed<br>@@ -886,7 +890,7 @@<br>         message = {<br>             'action': 'queues'<br>         }<br>-        #return self.collectDeferred(message, 'QueueStatusEnd')<br>+        # return self.collectDeferred(message, 'QueueStatusEnd')<br>         return self.sendDeferred(message).addCallback(self.errorUnlessResponse)<br> <br>     def queueStatus(self, queue=None, member=None):<br>@@ -1101,7 +1105,8 @@<br>     """<br>     protocol = AMIProtocol<br> <br>-    def __init__(self, username, secret, id=None, plaintext_login=True, on_reconnect=None):<br>+    def __init__(self, username, secret, id=None, plaintext_login=True,<br>+                 on_reconnect=None):<br>         self.username = username<br>         self.secret = secret<br>         self.id = id<br>diff --git a/tox.ini b/tox.ini<br>index a6c1ede..dd04c12 100644<br>--- a/tox.ini<br>+++ b/tox.ini<br>@@ -1,6 +1,9 @@<br> [tox]<br> envlist = py26,py27,py32,py33,py34,p35,pep8<br> <br>+[pep8]<br>+max-line-length=90<br>+<br> [testenv]<br> deps = -r{toxinidir}/tools/pip-requires<br>        -r{toxinidir}/tools/test-requires<br></pre><p>To view, visit <a href="https://gerrit.asterisk.org/8990">change 8990</a>. To unsubscribe, visit <a href="https://gerrit.asterisk.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.asterisk.org/8990"/><meta itemprop="name" content="View Change"/></div></div>

<div style="display:none"> Gerrit-Project: starpy </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-MessageType: merged </div>
<div style="display:none"> Gerrit-Change-Id: I293635427a48e66298042c3fc1e600e47854d3ed </div>
<div style="display:none"> Gerrit-Change-Number: 8990 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: Corey Farrell <git@cfware.com> </div>
<div style="display:none"> Gerrit-Reviewer: Corey Farrell <git@cfware.com> </div>
<div style="display:none"> Gerrit-Reviewer: George Joseph <gjoseph@digium.com> </div>
<div style="display:none"> Gerrit-Reviewer: Kevin Harwell <kharwell@digium.com> </div>