[Asterisk-code-review] Add support Python 3 (testsuite[master])

Rodrigo Ramirez Norambuena asteriskteam at digium.com
Tue Oct 11 17:17:32 CDT 2016


Rodrigo Ramirez Norambuena has uploaded a new change for review.

  https://gerrit.asterisk.org/4074

Change subject: Add support Python 3
......................................................................

Add support Python 3

Change-Id: I58546fd8c0cff97f73aa1e80ebb85851102008fb
---
M lib/python/asterisk/ami.py
M lib/python/asterisk/apptest.py
M lib/python/asterisk/ari.py
M lib/python/asterisk/astconfigparser.py
M lib/python/asterisk/astcsv.py
M lib/python/asterisk/astdicts.py
M lib/python/asterisk/asterisk.py
M lib/python/asterisk/bridge_test_case.py
M lib/python/asterisk/buildoptions.py
M lib/python/asterisk/cdr.py
M lib/python/asterisk/cel.py
M lib/python/asterisk/channel_test_condition.py
A lib/python/asterisk/compat.py
M lib/python/asterisk/confbridge.py
M lib/python/asterisk/fd_test_condition.py
M lib/python/asterisk/linkedid_check.py
M lib/python/asterisk/lock_test_condition.py
M lib/python/asterisk/originate.py
M lib/python/asterisk/pcap.py
M lib/python/asterisk/phones.py
M lib/python/asterisk/pjsua_mod.py
M lib/python/asterisk/pluggable_modules.py
M lib/python/asterisk/realtime_converter.py
M lib/python/asterisk/realtime_odbc_module.py
M lib/python/asterisk/realtime_test_module.py
M lib/python/asterisk/sip_channel_test_condition.py
M lib/python/asterisk/sip_dialog_test_condition.py
M lib/python/asterisk/sipp.py
M lib/python/asterisk/sippversion.py
M lib/python/asterisk/test_case.py
M lib/python/asterisk/test_conditions.py
M lib/python/asterisk/test_config.py
M lib/python/asterisk/test_suite_utils.py
M lib/python/asterisk/thread_test_condition.py
M lib/python/asterisk/version.py
M lib/python/asterisk/voicemail.py
M lib/python/pcap_listener.py
M lib/python/qm.py
M lib/python/rlmi.py
M lib/python/sip_message.py
R lib/python/test_runner.py
M runtests.py
M tests/cdr/cdr-tests.py
M tests/cdr/sqlite3/cdr_sqlite3.py
M tox.ini
M usage.py
46 files changed, 343 insertions(+), 236 deletions(-)


  git pull ssh://gerrit.asterisk.org:29418/testsuite refs/changes/74/4074/1

diff --git a/lib/python/asterisk/ami.py b/lib/python/asterisk/ami.py
index abed882..8373983 100644
--- a/lib/python/asterisk/ami.py
+++ b/lib/python/asterisk/ami.py
@@ -13,8 +13,9 @@
 import logging
 import re
 import json
-from pluggable_registry import PLUGGABLE_EVENT_REGISTRY,\
+from .pluggable_registry import PLUGGABLE_EVENT_REGISTRY,\
     PLUGGABLE_ACTION_REGISTRY, var_replace
+from . import compat
 
 LOGGER = logging.getLogger(__name__)
 
@@ -346,7 +347,7 @@
             lower_key = key.lower()
             if lower_key == 'extra':
                 value = dict((key.lower(), value)
-                             for key, value in value.iteritems())
+                             for key, value in compat.iteritems(value))
             self.requirements[lower_key] = value
         self.orderings = requirements.get('partialorder') or []
         self.named_id = requirements.get('id')
@@ -731,7 +732,7 @@
 
 def replace_ami_vars(mydict, values):
     outdict = {}
-    for key, value in mydict.iteritems():
+    for key, value in compat.iteritems(mydict):
         outdict[key] = var_replace(value, values)
 
     return outdict
diff --git a/lib/python/asterisk/apptest.py b/lib/python/asterisk/apptest.py
index b865b83..f40f15c 100644
--- a/lib/python/asterisk/apptest.py
+++ b/lib/python/asterisk/apptest.py
@@ -21,6 +21,7 @@
 from test_case import TestCase
 from ami import AMIEventInstance
 from version import AsteriskVersion
+import compat
 
 LOGGER = logging.getLogger(__name__)
 
@@ -171,7 +172,7 @@
         A Scenario is considered done if all results have been met and
         all expected actions have been executed.
         """
-        return (all(self._expected_results.itervalues()) and
+        return (all(compat.itervalues(self._expected_results)) and
                 all(i.ran_actions or i.unexpected
                     for i in self._event_instances))
 
diff --git a/lib/python/asterisk/ari.py b/lib/python/asterisk/ari.py
index 6241f13..c0a5f25 100644
--- a/lib/python/asterisk/ari.py
+++ b/lib/python/asterisk/ari.py
@@ -14,11 +14,12 @@
 import traceback
 import urllib
 
-from test_case import TestCase
-from pluggable_registry import PLUGGABLE_EVENT_REGISTRY,\
+from .test_case import TestCase
+from .pluggable_registry import PLUGGABLE_EVENT_REGISTRY,\
     PLUGGABLE_ACTION_REGISTRY, var_replace
-from test_suite_utils import all_match
+from .test_suite_utils import all_match
 from twisted.internet import reactor
+from . import compat
 try:
     from autobahn.websocket import WebSocketClientFactory, \
         WebSocketClientProtocol, connectWS
@@ -560,7 +561,7 @@
         url = self.ari.build_url(uri)
         requests_method = getattr(requests, self.method)
         params = dict((key, var_replace(val, values))
-                      for key, val in self.params.iteritems())
+                      for key, val in compat.iteritems(self.params))
 
         response = requests_method(
             url,
diff --git a/lib/python/asterisk/astconfigparser.py b/lib/python/asterisk/astconfigparser.py
index dc79e80..98a2493 100644
--- a/lib/python/asterisk/astconfigparser.py
+++ b/lib/python/asterisk/astconfigparser.py
@@ -5,11 +5,13 @@
 the GNU General Public License Version 2.
 """
 
+from __future__ import print_function
 import re
 import itertools
 
-from astdicts import OrderedDict
-from astdicts import MultiOrderedDict
+from .astdicts import OrderedDict
+from .astdicts import MultiOrderedDict
+from . import compat
 
 
 def merge_values(left, right, key):
@@ -270,11 +272,11 @@
 
 def write_dicts(config_file, mdicts):
     """Write the contents of the mdicts to the specified config file"""
-    for section, sect_list in mdicts.iteritems():
+    for section, sect_list in compat.itertools(mdicts):
         # every section contains a list of dictionaries
         for sect in sect_list:
             config_file.write("[%s]\n" % section)
-            for key, val_list in sect.iteritems():
+            for key, val_list in compat.iteritems(sect):
                 # every value is also a list
                 for v in val_list:
                     key_val = key
@@ -355,7 +357,7 @@
         if self._includes:
             res.extend(list(itertools.chain(*[
                 incl.get_sections(key, attr, searched)
-                for incl in self._includes.itervalues()])))
+                for incl in compat.itervalues(self._includes)])))
         if self._parent:
             res += self._parent.get_sections(key, attr, searched)
         return res
@@ -445,7 +447,7 @@
             with open(filename, 'rt') as config_file:
                 self._read(config_file, sect)
         except IOError:
-            print "Could not open file ", filename, " for reading"
+            print("Could not open file ", filename, " for reading")
 
     def _read(self, config_file, sect):
         """Parse configuration information from the config_file"""
@@ -478,7 +480,7 @@
     def write(self, config_file):
         """Write configuration information out to a file"""
         try:
-            for key, val in self._includes.iteritems():
+            for key, val in compat.iteritems(self._includes):
                 val.write(key)
                 config_file.write('#include "%s"\n' % key)
 
@@ -490,4 +492,4 @@
                 with open(config_file, 'wt') as fp:
                     self.write(fp)
             except IOError:
-                print "Could not open file ", config_file, " for writing"
+                print("Could not open file ", config_file, " for writing")
diff --git a/lib/python/asterisk/astcsv.py b/lib/python/asterisk/astcsv.py
index 67d25d3..97d11a5 100644
--- a/lib/python/asterisk/astcsv.py
+++ b/lib/python/asterisk/astcsv.py
@@ -16,6 +16,7 @@
 import csv
 import re
 import logging
+from . import compat
 
 LOGGER = logging.getLogger(__name__)
 
@@ -72,7 +73,7 @@
 
     def iteritems(self):
         """Iterate over the values in the columns"""
-        return self.__columns.iteritems()
+        return compat.iteritems(self.__columns)
 
     def __str__(self):
         return ",".join(["\"%s\"" % (self.__dict__[x]) for x in self.__fields])
@@ -97,7 +98,8 @@
 
         try:
             csvreader = csv.DictReader(open(self.filename, "r"), fields, ",")
-        except IOError as (errno, strerror):
+        except IOError as err:
+            (errno, strerror) = err.args
             LOGGER.error("IOError %d[%s] while opening file '%s'" %
                          (errno, strerror, self.filename))
         except:
@@ -147,7 +149,7 @@
             size = len(list_a)
 
             # attempt two orderings: forward and reversed
-            guess_orders = (range(size), list(reversed(range(size))))
+            guess_orders = (list(range(size)), list(reversed(list(range(size)))))
             found_orders = []
 
             for guess_order in guess_orders:
diff --git a/lib/python/asterisk/astdicts.py b/lib/python/asterisk/astdicts.py
index ae63075..bfe6e1b 100644
--- a/lib/python/asterisk/astdicts.py
+++ b/lib/python/asterisk/astdicts.py
@@ -2,10 +2,19 @@
 # Passes Python2.7's test suite and incorporates all the latest updates.
 # copied from http://code.activestate.com/recipes/576693/
 
-try:
-    from thread import get_ident as _get_ident
-except ImportError:
-    from dummy_thread import get_ident as _get_ident
+from . import compat
+
+if compat.PY3:
+    try:
+        from _thread import get_ident as _get_ident
+    except ImportError:
+        from _dummy_thread import get_ident as _get_ident
+else:
+    try:
+        from thread import get_ident as _get_ident
+    except ImportError:
+        from dummy_thread import get_ident as _get_ident
+
 
 try:
     from _abcoll import KeysView, ValuesView, ItemsView
@@ -203,7 +212,7 @@
         try:
             if not self:
                 return '%s()' % (self.__class__.__name__,)
-            return '%s(%r)' % (self.__class__.__name__, self.items())
+            return '%s(%r)' % (self.__class__.__name__, list(self.items()))
         finally:
             del _repr_running[call_key]
 
@@ -238,7 +247,7 @@
 
         '''
         if isinstance(other, OrderedDict):
-            return len(self)==len(other) and self.items() == other.items()
+            return len(self)==len(other) and list(self.items()) == list(other.items())
         return dict.__eq__(self, other)
 
     def __ne__(self, other):
diff --git a/lib/python/asterisk/asterisk.py b/lib/python/asterisk/asterisk.py
index b98f2ce..9305158 100755
--- a/lib/python/asterisk/asterisk.py
+++ b/lib/python/asterisk/asterisk.py
@@ -17,10 +17,12 @@
 import shutil
 import logging
 
-import test_suite_utils
+from . import test_suite_utils
 
-from config import ConfigFile
-from version import AsteriskVersion
+from .config import ConfigFile
+from .version import AsteriskVersion
+
+from . import compat
 
 from twisted.internet import reactor, protocol, defer, utils, error
 from twisted.python.failure import Failure
@@ -232,7 +234,7 @@
 
     def outReceived(self, data):
         """Override of ProcessProtocol.outReceived"""
-        self.output += data
+        self.output += str(data)
 
     def connectionMade(self):
         """Override of ProcessProtocol.connectionMade"""
@@ -331,7 +333,7 @@
         default_etc_directory = os.path.join(localtest_root, "etc/asterisk")
     else:
         # select tmp path with most available space
-        best_tmp = sorted(['/tmp', '/var/tmp'], cmp=compare_free_space)[0]
+        best_tmp = sorted(['/tmp', '/var/tmp'], key=compat.cmp_to_key(compare_free_space))[0]
         # Base location of the temporary files created by the testsuite
         test_suite_root = best_tmp + "/asterisk-testsuite"
         # The default etc directory for Asterisk
@@ -477,7 +479,7 @@
         def __wait_fully_booted_callback(cli_command):
             """Callback for CLI command waitfullybooted"""
 
-            if "Asterisk has fully booted" in cli_command.output:
+            if "Asterisk has fully booted" in cli_command.output.decode('utf-8'):
                 msg = "Successfully started Asterisk %s" % self.host
                 self._start_deferred.callback(msg)
             else:
@@ -941,7 +943,7 @@
                 ast_file.write("#include \"%s/asterisk.options.conf.inc\"\n" %
                                (self.astetcdir))
                 if ast_conf_options:
-                    for (var, val) in ast_conf_options.iteritems():
+                    for (var, val) in compat.iteritems(ast_conf_options):
                         ast_file.write("%s = %s\n" % (var, val))
                 for (var, val) in cat.options:
                     if not ast_conf_options or var not in ast_conf_options:
diff --git a/lib/python/asterisk/bridge_test_case.py b/lib/python/asterisk/bridge_test_case.py
index ea1767e..155b3a1 100644
--- a/lib/python/asterisk/bridge_test_case.py
+++ b/lib/python/asterisk/bridge_test_case.py
@@ -13,7 +13,7 @@
 from time import sleep
 
 sys.path.append("lib/python")
-from test_case import TestCase
+from .test_case import TestCase
 
 LOGGER = logging.getLogger(__name__)
 
diff --git a/lib/python/asterisk/buildoptions.py b/lib/python/asterisk/buildoptions.py
index 3b0b4b9..0229e17 100644
--- a/lib/python/asterisk/buildoptions.py
+++ b/lib/python/asterisk/buildoptions.py
@@ -51,7 +51,7 @@
         except IOError:
             return ret_val
         except:
-            print "Unexpected error: %s" % sys.exc_info()[0]
+            print("Unexpected error: %s" % sys.exc_info()[0])
             return ret_val
         for line in file_lines:
             if "#define" in line:
@@ -59,7 +59,7 @@
                 if (define_tuple[0] == "" or define_tuple[2] == ""):
                     msg = ("Unable to parse build option line [%s] into "
                            "compiler flag token and value" % line)
-                    print msg
+                    print(msg)
                 else:
                     flag = define_tuple[0].strip()
                     allowed = define_tuple[2].strip()
@@ -84,7 +84,7 @@
         True if the build option exists and the expected value matches;
         false otherwise
         """
-        if build_option in self._flags.keys():
+        if build_option in list(self._flags.keys()):
             return expected_value == self._flags[build_option]
         elif expected_value == "0":
             return True
diff --git a/lib/python/asterisk/cdr.py b/lib/python/asterisk/cdr.py
index cc5226b..003734f 100644
--- a/lib/python/asterisk/cdr.py
+++ b/lib/python/asterisk/cdr.py
@@ -12,7 +12,7 @@
 
 import unittest
 import sys
-import astcsv
+from . import astcsv
 import logging
 
 LOGGER = logging.getLogger(__name__)
diff --git a/lib/python/asterisk/cel.py b/lib/python/asterisk/cel.py
index d22d67f..104397e 100644
--- a/lib/python/asterisk/cel.py
+++ b/lib/python/asterisk/cel.py
@@ -12,7 +12,7 @@
 
 import yaml
 import unittest
-import astcsv
+from . import astcsv
 import re
 import logging
 
@@ -50,7 +50,7 @@
 
     def stop_handler(self, reason):
         """Write out the file. Currently hard codd to cel_events.yaml"""
-        stream = file('cel_events.yaml', 'w')
+        stream = open('cel_events.yaml', 'w')
         if len(self.display) == 0:
             yaml.dump(self.events, stream)
         else:
diff --git a/lib/python/asterisk/channel_test_condition.py b/lib/python/asterisk/channel_test_condition.py
index ec1d774..5905f34 100644
--- a/lib/python/asterisk/channel_test_condition.py
+++ b/lib/python/asterisk/channel_test_condition.py
@@ -9,7 +9,7 @@
 """
 
 from twisted.internet import defer
-from test_conditions import TestCondition
+from .test_conditions import TestCondition
 import logging
 import unittest
 import re
diff --git a/lib/python/asterisk/compat.py b/lib/python/asterisk/compat.py
new file mode 100644
index 0000000..0630ba7
--- /dev/null
+++ b/lib/python/asterisk/compat.py
@@ -0,0 +1,61 @@
+import sys
+# Ported for sorted cmp old way
+# https://docs.python.org/3/howto/sorting.html
+def cmp_to_key(mycmp):
+    'Convert a cmp= function into a key= function'
+    class K:
+        def __init__(self, obj, *args):
+            self.obj = obj
+
+        def __lt__(self, other):
+            return mycmp(self.obj, other.obj) < 0
+
+        def __gt__(self, other):
+            return mycmp(self.obj, other.obj) > 0
+
+        def __eq__(self, other):
+            return mycmp(self.obj, other.obj) == 0
+
+        def __le__(self, other):
+            return mycmp(self.obj, other.obj) <= 0
+
+        def __ge__(self, other):
+            return mycmp(self.obj, other.obj) >= 0
+
+        def __ne__(self, other):
+            return mycmp(self.obj, other.obj) != 0
+    return K
+
+PY3 = sys.version_info[0] == 3
+
+if PY3:
+    def itervalues(d, **kw):
+        return iter(d.values(**kw))
+
+    def xrange(*args, **kwargs):
+        return iter(range(*args, **kwargs))
+
+    def iteritems(d, **kw):
+        return iter(d.items(**kw))
+
+    unicode = str
+else:
+    def itervalues(d, **kw):
+        return d.itervalues(**kw)
+
+    def iteritems(d, **kw):
+        return d.iteritems(**kw)
+
+    xrange = xrange
+    unicode = unicode
+
+def with_metaclass(meta, *bases):
+    """Create a base class with a metaclass."""
+    # This requires a bit of explanation: the basic idea is to make a dummy
+    # metaclass for one level of class instantiation that replaces itself with
+    # the actual metaclass.
+    class metaclass(meta):
+
+        def __new__(cls, name, this_bases, d):
+            return meta(name, bases, d)
+    return type.__new__(metaclass, 'temporary_class', (), {})
diff --git a/lib/python/asterisk/confbridge.py b/lib/python/asterisk/confbridge.py
index acca1e7..0ca364d 100644
--- a/lib/python/asterisk/confbridge.py
+++ b/lib/python/asterisk/confbridge.py
@@ -14,7 +14,7 @@
 import sys
 import logging
 
-from test_state import TestState
+from .test_state import TestState
 from twisted.internet import reactor
 
 sys.path.append("lib/python")
diff --git a/lib/python/asterisk/fd_test_condition.py b/lib/python/asterisk/fd_test_condition.py
index 4a8f869..234af28 100644
--- a/lib/python/asterisk/fd_test_condition.py
+++ b/lib/python/asterisk/fd_test_condition.py
@@ -11,7 +11,7 @@
 import logging
 
 from twisted.internet import defer
-from test_conditions import TestCondition
+from .test_conditions import TestCondition
 
 LOGGER = logging.getLogger(__name__)
 
diff --git a/lib/python/asterisk/linkedid_check.py b/lib/python/asterisk/linkedid_check.py
index 83a7aa6..93e0f9f 100644
--- a/lib/python/asterisk/linkedid_check.py
+++ b/lib/python/asterisk/linkedid_check.py
@@ -163,7 +163,7 @@
         event   - The event key/value pairs in a dict
         """
         uniqueid = event['uniqueid']
-        if uniqueid not in self.channels.keys():
+        if uniqueid not in list(self.channels.keys()):
             new_chan = LinkedIdChannel(uniqueid,
                                        len(self.channels) + 1,
                                        self.test_object)
@@ -185,7 +185,7 @@
         # insure the channel object is created for a new uniqueid
         uniqueid = event['uniqueid']
         linkedid = event['linkedid']
-        if not uniqueid in self.channels.keys():
+        if not uniqueid in list(self.channels.keys()):
             new_chan = LinkedIdChannel(uniqueid,
                                        len(self.channels) + 1,
                                        self.test_object)
@@ -216,7 +216,7 @@
         bridgeid = event['bridgeuniqueid']
 
         # uniqueid bridges numerically in logs for better readability
-        if bridgeid not in self.bridges.keys():
+        if bridgeid not in list(self.bridges.keys()):
             self.bridges[bridgeid] = len(self.bridges) + 1
 
         # list of channels in this bridge
diff --git a/lib/python/asterisk/lock_test_condition.py b/lib/python/asterisk/lock_test_condition.py
index 0296693..f9d1a4e 100644
--- a/lib/python/asterisk/lock_test_condition.py
+++ b/lib/python/asterisk/lock_test_condition.py
@@ -13,7 +13,7 @@
 import unittest
 
 from twisted.internet import defer
-from test_conditions import TestCondition
+from .test_conditions import TestCondition
 
 LOGGER = logging.getLogger(__name__)
 
diff --git a/lib/python/asterisk/originate.py b/lib/python/asterisk/originate.py
index e6befb5..6c87a57 100644
--- a/lib/python/asterisk/originate.py
+++ b/lib/python/asterisk/originate.py
@@ -16,7 +16,7 @@
 import json
 import requests
 
-import ari
+from . import ari
 
 LOGGER = logging.getLogger(__name__)
 
diff --git a/lib/python/asterisk/pcap.py b/lib/python/asterisk/pcap.py
index 91031f5..41148ac 100644
--- a/lib/python/asterisk/pcap.py
+++ b/lib/python/asterisk/pcap.py
@@ -702,13 +702,14 @@
         self.callbacks = {}
         self.traces = {}
 
-    def process_packet(self, packet, (host, port)):
+    def process_packet(self, packet, host_port):
         """Store a known packet in our traces and call our callbacks
 
         Keyword Arguments:
         packet       A raw packet received from ... something.
         (host, port) Tuple of received host and port
         """
+        (host, port) = host_port
         packet = self.packet_factory.interpret_packet(packet)
         if packet is None:
             return
@@ -775,13 +776,14 @@
             self.rules = rules
             self.cb = cb
 
-        def datagramReceived(self, data, (host, port)):
+        def datagramReceived(self, data, host_port):
             """Callback for when a datagram is received
 
             Keyword Arguments:
             data         The actual packet
             (host, port) Tuple of source host and port
             """
+            (host, port) = host_port
             LOGGER.debug('Proxy received from {0}:{1}\n{2}'.format(
                 host, port, data))
 
diff --git a/lib/python/asterisk/phones.py b/lib/python/asterisk/phones.py
index c4822eb..49e9716 100755
--- a/lib/python/asterisk/phones.py
+++ b/lib/python/asterisk/phones.py
@@ -13,7 +13,8 @@
 from twisted.internet import reactor, task
 import pjsua as pj
 
-import pjsua_mod
+from . import pjsua_mod
+from . import compat
 
 LOGGER = logging.getLogger(__name__)
 
@@ -85,7 +86,7 @@
 
     def __setup_pjsua_acc_cb(self):
         """Setup PJSUA account callbacks"""
-        for name, phone_obj in self.__pjsua_phones.iteritems():
+        for name, phone_obj in compat.iteritems(self.__pjsua_phones):
             acc_cb = AccCallback()
             phone_obj.account.set_callback(acc_cb)
             LOGGER.info("%s is ready to receive calls." % name)
@@ -106,7 +107,7 @@
         if name:
             return self.__pjsua_phones.get(name)
         if account:
-            for name, phone_obj in self.__pjsua_phones.iteritems():
+            for name, phone_obj in compat.iteritems(self.__pjsua_phones):
                 if account is phone_obj.account:
                     return phone_obj
 
diff --git a/lib/python/asterisk/pjsua_mod.py b/lib/python/asterisk/pjsua_mod.py
index 63add6a..6f997a8 100644
--- a/lib/python/asterisk/pjsua_mod.py
+++ b/lib/python/asterisk/pjsua_mod.py
@@ -147,7 +147,7 @@
             self.lib.set_null_snd_dev()
             self.__create_accounts()
             self.lib.start()
-        except pj.Error, exception:
+        except pj.Error as exception:
             LOGGER.error("Exception: " + str(exception))
             self.lib.destroy()
             self.lib = None
diff --git a/lib/python/asterisk/pluggable_modules.py b/lib/python/asterisk/pluggable_modules.py
index 785874f..81a31e1 100755
--- a/lib/python/asterisk/pluggable_modules.py
+++ b/lib/python/asterisk/pluggable_modules.py
@@ -14,13 +14,14 @@
 import re
 
 sys.path.append("lib/python")
-from ami import AMIEventInstance
+from .ami import AMIEventInstance
 from twisted.internet import reactor
 from starpy import fastagi
 from test_runner import load_and_parse_module
-from pluggable_registry import PLUGGABLE_ACTION_REGISTRY,\
+from .pluggable_registry import PLUGGABLE_ACTION_REGISTRY,\
     PLUGGABLE_EVENT_REGISTRY,\
     PluggableRegistry
+from . import compat
 
 LOGGER = logging.getLogger(__name__)
 
@@ -373,7 +374,7 @@
                         ["astspooldir"], call_file_num))
 
         with open(self.locale, 'w') as outfile:
-            for key, value in params.items():
+            for key, value in compat.iteritems(params):
                 outfile.write("%s: %s\n" % (key, value))
         LOGGER.debug("Wrote call file to %s", self.locale)
         self.move_file(call_file_num, call_file)
@@ -570,7 +571,7 @@
             return
 
         current_trigger = config['trigger']['match']
-        for key, value in current_trigger.iteritems():
+        for key, value in compat.iteritems(current_trigger):
             if key.lower() not in event:
                 LOGGER.debug("Condition %s not in event, returning", key)
                 return
@@ -778,7 +779,7 @@
 
         def register_modules(config, registry):
             """Register pluggable modules into the registry"""
-            for key, local_class_path in config.iteritems():
+            for key, local_class_path in compat.iteritems(config.items):
                 local_class = load_and_parse_module(local_class_path)
                 if not local_class:
                     raise Exception("Unable to load %s for module key %s"
@@ -798,7 +799,7 @@
         for e_a_set in config["mapping"]:
             plug_set = {"events": [], "actions": []}
 
-            for plug_name, plug_config in e_a_set.iteritems():
+            for plug_name, plug_config in compat.iteritems(e_a_set.items):
                 self.parse_module_config(plug_set, plug_name, plug_config)
 
             if 0 == len(plug_set["events"]):
diff --git a/lib/python/asterisk/realtime_converter.py b/lib/python/asterisk/realtime_converter.py
index ca7e688..e131eb4 100644
--- a/lib/python/asterisk/realtime_converter.py
+++ b/lib/python/asterisk/realtime_converter.py
@@ -9,8 +9,9 @@
 import os
 from sqlalchemy import create_engine, MetaData, Table
 
-import astconfigparser
-import logging
+from . import astconfigparser
+from . import compat
+from . import logging
 
 LOGGER = logging.getLogger(__name__)
 
@@ -83,8 +84,8 @@
         self.filename = filename
         self.sections = sections
         # All affected database tables in list form. Used for convenience
-        self.tables = [table for section in sections.itervalues() for table in
-                       section.itervalues()]
+        self.tables = [table for section in compat.itervalues(sections) for table in
+                       compat.itervalues(section)]
         self.sorcery = None
         self.extconfig = None
 
@@ -111,9 +112,9 @@
         config_dir: The directory where Asterisk configuration can be found
         """
         with open(self.sorcery.file, 'a') as sorcery:
-            for section, items in self.sections.iteritems():
+            for section, items in compat.iteritems(self.sections):
                 sorcery.write('[{0}]\n'.format(section))
-                for obj, table in items.iteritems():
+                for obj, table in compat.iteritems(items):
                     sorcery.write('{0} = realtime,{1}\n'.format(obj, table))
 
     def write_extconfig_conf(self):
@@ -141,7 +142,7 @@
         """
         conf = astconfigparser.MultiOrderedConfigParser()
         conf.read(os.path.join(config_dir, self.filename))
-        for title, sections in conf.sections().iteritems():
+        for title, sections in compat.iteritems(conf.sections()):
             LOGGER.info("Inspecting objects with title {0}".format(title))
             for section in sections:
                 obj_type = section.get('type')[0]
@@ -165,7 +166,7 @@
         Keyword Arguments:
         obj_type: The object type to find the section for
         """
-        for section, contents in self.sections.iteritems():
+        for section, contents in compat.iteritems(self.sections):
             if obj_type in contents:
                 return section
 
diff --git a/lib/python/asterisk/realtime_odbc_module.py b/lib/python/asterisk/realtime_odbc_module.py
index 3653553..b79869b 100644
--- a/lib/python/asterisk/realtime_odbc_module.py
+++ b/lib/python/asterisk/realtime_odbc_module.py
@@ -8,7 +8,7 @@
 """
 import os
 import logging
-
+from . import compat
 
 LOGGER = logging.getLogger(__name__)
 
@@ -53,7 +53,7 @@
         self.res_odbc = {}
 
         # generate configuration for each dsn
-        for dsn, config in module_config.iteritems():
+        for dsn, config in compat.iteritems(module_config):
             self._configure(dsn, config)
 
         # set the odbc and conf files
@@ -109,7 +109,7 @@
         with open(filepath, 'w') as filehandle:
             for section in contents:
                 filehandle.write('[' + section + ']\n')
-                for name, value in contents[section].iteritems():
+                for name, value in compat.iteritems(contents[section]):
                     filehandle.write(name + '=' + value + '\n')
 
     def _read_ini_file(self, filepath):
diff --git a/lib/python/asterisk/realtime_test_module.py b/lib/python/asterisk/realtime_test_module.py
index fd38aa9..3b059a2 100644
--- a/lib/python/asterisk/realtime_test_module.py
+++ b/lib/python/asterisk/realtime_test_module.py
@@ -15,6 +15,7 @@
 from twisted.internet import error
 from twisted.web.server import Site
 from twisted.web.resource import Resource, NoResource
+from . import compat
 
 LOGGER = logging.getLogger(__name__)
 
@@ -91,7 +92,7 @@
 
         return [row for row in table
                 if all(key in row and re.match(value, row[key])
-                       for key, value in where.iteritems())]
+                       for key, value in compat.iteritems(where.items))]
 
     def retrieve_rows(self, table_name, where):
         """Retrieve multiple rows from a table.
@@ -239,7 +240,7 @@
         since we could use a dict comprehension.
         """
         filtered_args = {}
-        for key, values in args.iteritems():
+        for key, values in compat.iteritems(args):
             if " LIKE" in key:
                 # Strip away " LIKE" and % from values
                 filtered_args[key[:-5]] = [val.replace('%', '.*')
@@ -250,7 +251,7 @@
         LOGGER.debug('filtered args is %s' % filtered_args)
 
         return dict((key, values[0] if values else '.*') for key, values in
-                    filtered_args.iteritems())
+                    compat.iteritems(filtered_args))
 
     def encode_row(self, row):
         """Encode a retrieved row for an HTTP response.
@@ -261,7 +262,7 @@
         Example output: 'foo=cat&bar=dog&baz=donkey'
         """
         string = '&'.join(['{0}={1}'.format(cgi.escape(key), cgi.escape(val))
-                           for key, val in row.iteritems()])
+                           for key, val in compat.iteritems(row)])
         LOGGER.debug("Returning response %s" % string)
         return string
 
@@ -481,7 +482,7 @@
         if not data:
             return
 
-        for table_name, rows in data.iteritems():
+        for table_name, rows in compat.iteritems(data):
             self.rt_data.add_rows(table_name, rows)
 
     def setup_http(self):
diff --git a/lib/python/asterisk/sip_channel_test_condition.py b/lib/python/asterisk/sip_channel_test_condition.py
index 150d90f..0f265f0 100644
--- a/lib/python/asterisk/sip_channel_test_condition.py
+++ b/lib/python/asterisk/sip_channel_test_condition.py
@@ -10,7 +10,7 @@
 """
 
 from twisted.internet import defer
-from test_conditions import TestCondition
+from .test_conditions import TestCondition
 
 
 class SipChannelTestCondition(TestCondition):
diff --git a/lib/python/asterisk/sip_dialog_test_condition.py b/lib/python/asterisk/sip_dialog_test_condition.py
index 4c75710..6b681d3 100644
--- a/lib/python/asterisk/sip_dialog_test_condition.py
+++ b/lib/python/asterisk/sip_dialog_test_condition.py
@@ -12,7 +12,7 @@
 import logging.config
 import unittest
 
-from test_conditions import TestCondition
+from .test_conditions import TestCondition
 from twisted.internet import defer
 
 LOGGER = logging.getLogger(__name__)
diff --git a/lib/python/asterisk/sipp.py b/lib/python/asterisk/sipp.py
index 59aa54b..2fec534 100644
--- a/lib/python/asterisk/sipp.py
+++ b/lib/python/asterisk/sipp.py
@@ -10,19 +10,19 @@
 """
 
 import logging
-import test_suite_utils
+from . import test_suite_utils
+from . import compat
 
 from abc import ABCMeta, abstractmethod
 from twisted.internet import reactor, defer, protocol, error
-from test_case import TestCase
+from .test_case import TestCase
 
 LOGGER = logging.getLogger(__name__)
 
 
-class ScenarioGenerator(object):
+class ScenarioGenerator(compat.with_metaclass(ABCMeta, object)):
     """Scenario Generators provide a generator function for creating scenario
     sets for use by SIPpTestCase"""
-    __metaclass__ = ABCMeta
 
     @abstractmethod
     def generator(self):
@@ -486,7 +486,7 @@
     def outReceived(self, data):
         """Override of ProcessProtocol.outReceived"""
         LOGGER.debug("Received from SIPp scenario %s: %s" % (self._name, data))
-        self.output += data
+        self.output += str(data)
 
     def connectionMade(self):
         """Override of ProcessProtocol.connectionMade"""
diff --git a/lib/python/asterisk/sippversion.py b/lib/python/asterisk/sippversion.py
index b1110fc..9ca5e99 100644
--- a/lib/python/asterisk/sippversion.py
+++ b/lib/python/asterisk/sippversion.py
@@ -13,7 +13,7 @@
 import unittest
 sys.path.append("lib/python")
 
-import test_suite_utils
+from . import test_suite_utils
 
 
 class SIPpVersion:
@@ -48,6 +48,7 @@
             except OSError:
                 return
             for line in sipp_process.stdout:
+                line = line.decode('utf-8') # py3 support
                 if line.strip().startswith('SIPp '):
                     sipp = line.strip()[5:]
                     sipp = sipp.split(',', 1)
@@ -56,7 +57,6 @@
                     if len(sipp) > 1:
                         feature = sipp[1]
             sipp_process.wait()
-
         if version is not None:
             self.__parse_version(version)
         if feature is not None:
diff --git a/lib/python/asterisk/test_case.py b/lib/python/asterisk/test_case.py
index 9fc07cc..e904aae 100644
--- a/lib/python/asterisk/test_case.py
+++ b/lib/python/asterisk/test_case.py
@@ -7,6 +7,7 @@
 the GNU General Public License Version 2.
 """
 
+from __future__ import print_function
 import sys
 import logging
 import logging.config
@@ -19,10 +20,10 @@
 from twisted.python import log
 from starpy import manager, fastagi
 
-from asterisk import Asterisk
-from test_config import TestConfig
-from test_conditions import TestConditionController
-from version import AsteriskVersion
+from .asterisk import Asterisk
+from .test_config import TestConfig
+from .test_conditions import TestConditionController
+from .version import AsteriskVersion
 
 
 try:
@@ -46,12 +47,12 @@
         except:
             msg = ("WARNING: failed to preserve existing loggers - some "
                    "logging statements may be missing")
-            print msg
+            print(msg)
             logging.config.fileConfig(config_file)
     else:
         msg = ("WARNING: no logging.conf file found; using default "
                "configuration")
-        print msg
+        print(msg)
         logging.basicConfig(level=logging.DEBUG)
 
     root_logger = logging.getLogger()
@@ -103,7 +104,7 @@
         # for the rasterisk CLI connection. As a quick fix, we hash the path
         # using md5, to make it unique enough.
         self.realbase = self.test_name.replace("tests/", "", 1)
-        self.base = md5(self.realbase).hexdigest()
+        self.base = md5(self.realbase.encode('utf-8')).hexdigest()
         # We provide a symlink to it from a named path.
         named_dir = os.path.join(Asterisk.test_suite_root, self.realbase)
         try:
@@ -236,7 +237,7 @@
             asterisks = self.global_config.config.get('asterisk-instances')
         else:
             asterisks = [{'num': i + 1, 'host': '127.0.0.%d' % (i + 1)}
-                         for i in range(count)]
+                         for i in list(range(count))]
         return asterisks
 
     def create_asterisk(self, count=1, base_configs_path=None):
diff --git a/lib/python/asterisk/test_conditions.py b/lib/python/asterisk/test_conditions.py
index dbf36a7..7053465 100644
--- a/lib/python/asterisk/test_conditions.py
+++ b/lib/python/asterisk/test_conditions.py
@@ -19,7 +19,7 @@
 import logging
 import logging.config
 
-from buildoptions import AsteriskBuildOptions
+from .buildoptions import AsteriskBuildOptions
 from twisted.internet import defer
 
 LOGGER = logging.getLogger(__name__)
diff --git a/lib/python/asterisk/test_config.py b/lib/python/asterisk/test_config.py
index b44ffb9..e04cd5b 100644
--- a/lib/python/asterisk/test_config.py
+++ b/lib/python/asterisk/test_config.py
@@ -9,6 +9,7 @@
 the GNU General Public License Version 2.
 """
 
+from __future__ import print_function
 import sys
 import os
 import subprocess
@@ -18,12 +19,12 @@
 
 sys.path.append("lib/python")
 
-import test_suite_utils
+from . import test_suite_utils
 
-from version import AsteriskVersion
-from asterisk import Asterisk
-from buildoptions import AsteriskBuildOptions
-from sippversion import SIPpVersion
+from .version import AsteriskVersion
+from .asterisk import Asterisk
+from .buildoptions import AsteriskBuildOptions
+from .sippversion import SIPpVersion
 
 
 class TestConditionConfig(object):
@@ -124,7 +125,7 @@
                 feature = dep['sipp']['feature']
             self.sipp_version = SIPpVersion()
             self.version = SIPpVersion(version, feature)
-            if self.sipp_version >= self.version:
+            if str(self.sipp_version) >= str(self.version):
                 self.met = True
             if self.version.tls and not self.sipp_version.tls:
                 self.met = False
@@ -139,7 +140,7 @@
                     self.met = getattr(self, dir_method)()
                     found = True
             if not found:
-                print "Unknown custom dependency - '%s'" % self.name
+                print(("Unknown custom dependency - '%s'" % self.name))
         elif "asterisk" in dep:
             if self.ast:
                 self.name = dep["asterisk"]
@@ -156,12 +157,12 @@
                 self.met = True
         elif "pcap" in dep:
             self.name = "pcap"
-            from test_case import PCAP_AVAILABLE
+            from .test_case import PCAP_AVAILABLE
             self.met = PCAP_AVAILABLE
         else:
-            print "Unknown dependency type specified:"
+            print("Unknown dependency type specified:")
             for key in dep.keys():
-                print key
+                print(key)
 
     def depend_remote(self):
         """Check to see if we run against a remote instance of Asterisk"""
@@ -248,13 +249,13 @@
         if self.asterisk_build_options:
             return (self.asterisk_build_options.check_option(name))
         else:
-            print "Unable to evaluate build options: no build options found"
+            print("Unable to evaluate build options: no build options found")
             return False
 
     def _find_asterisk_module(self, name):
         """Determine if an Asterisk module exists"""
         if not Dependency.ast:
-            print "Unable to evaluate Asterisk modules: Asterisk not found"
+            print("Unable to evaluate Asterisk modules: Asterisk not found")
             return False
 
         if Dependency.ast.original_astmoddir == "":
@@ -303,12 +304,12 @@
         try:
             self._parse_config()
         except yaml.YAMLError as e:
-            print("YAML Parse Error: %s" % e)
-            print(traceback.format_exc())
+            print(("YAML Parse Error: %s" % e))
+            print((traceback.format_exc()))
             self.can_run = False
         except:
-            print("Exception occurred while parsing config:", sys.exc_info()[0])
-            print(traceback.format_exc())
+            print(("Exception occurred while parsing config:", sys.exc_info()[0]))
+            print((traceback.format_exc()))
             self.can_run = False
 
     def _process_global_settings(self):
@@ -331,8 +332,8 @@
                 if self.config is not None and 'exclude-tests' in self.config:
                     self.excluded_tests = self.config['exclude-tests']
             else:
-                print ("WARNING - test configuration [%s] not found in "
-                       "config file" % self.test_configuration)
+                print(("WARNING - test configuration [%s] not found in "
+                       "config file" % self.test_configuration))
 
     def _process_testinfo(self):
         """Process the test information block"""
@@ -395,8 +396,8 @@
             self.config = yaml.load(config_file)
 
         if not self.config:
-            print "ERROR: Failed to load configuration for test '%s'" % \
-                self.test_name
+            print("ERROR: Failed to load configuration for test '%s'" % \
+                self.test_name)
             return
 
         self._process_global_settings()
@@ -422,8 +423,8 @@
             matches = [cond_def for cond_def in self.condition_definitions
                        if cond_def['name'] == conf['name']]
             if len(matches) != 1:
-                print ("Unknown or too many matches for condition: " +
-                       conf['name'])
+                print(("Unknown or too many matches for condition: " +
+                       conf['name']))
             else:
                 pre_cond = TestConditionConfig(conf, matches[0], "Pre")
                 post_cond = TestConditionConfig(conf, matches[0], "Post")
diff --git a/lib/python/asterisk/test_suite_utils.py b/lib/python/asterisk/test_suite_utils.py
index d8ef541..257292c 100644
--- a/lib/python/asterisk/test_suite_utils.py
+++ b/lib/python/asterisk/test_suite_utils.py
@@ -19,7 +19,8 @@
 from os import remove
 from shutil import move
 from tempfile import mkstemp
-from config import ConfigFile
+from .config import ConfigFile
+from . import compat
 
 LOGGER = logging.getLogger(__name__)
 
@@ -100,12 +101,12 @@
     elif isinstance(pattern, dict):
         # Dict should match for every field in the pattern.
         # extra fields in the message are fine.
-        for key, value in pattern.iteritems():
+        for key, value in compat.iteritems(pattern):
             to_check = message.get(key)
             if to_check is None or not all_match(value, to_check):
                 return False
         return True
-    elif isinstance(pattern, str) or isinstance(pattern, unicode):
+    elif isinstance(pattern, str) or isinstance(pattern, compat.unicode):
         # Pattern strings are considered to be regexes
         return re.match(pattern, str(message)) is not None
     elif isinstance(pattern, int):
diff --git a/lib/python/asterisk/thread_test_condition.py b/lib/python/asterisk/thread_test_condition.py
index 0b622ea..c6abf63 100644
--- a/lib/python/asterisk/thread_test_condition.py
+++ b/lib/python/asterisk/thread_test_condition.py
@@ -9,8 +9,8 @@
 """
 
 import logging
-from test_conditions import TestCondition
-from version import AsteriskVersion
+from .test_conditions import TestCondition
+from .version import AsteriskVersion
 from twisted.internet import defer
 
 LOGGER = logging.getLogger(__name__)
diff --git a/lib/python/asterisk/version.py b/lib/python/asterisk/version.py
index 5c25032..9ce86e4 100644
--- a/lib/python/asterisk/version.py
+++ b/lib/python/asterisk/version.py
@@ -18,7 +18,7 @@
 import sys
 import subprocess
 
-import test_suite_utils
+from . import test_suite_utils
 
 LOGGER = logging.getLogger(__name__)
 
@@ -238,7 +238,7 @@
     def __int__(self):
         """Convert the Asterisk version to an integer for comparisons"""
         if self.name:
-            return sys.maxint
+            return sys.maxsize if sys.version_info > (3,) else sys.maxint
         elif (self.branch):
             # Branches are a little odd. The more you specify, the less your
             # calculated value is. This keeps the following relationships true:
@@ -363,6 +363,7 @@
                                                    o_excep.strerror))
                 raise
             process.wait()
+            version  = version.decode('utf-8') # compat py3
             cls._asterisk_version_from_binary = version.replace(
                 "Asterisk ", "")
         return cls._asterisk_version_from_binary
diff --git a/lib/python/asterisk/voicemail.py b/lib/python/asterisk/voicemail.py
index 8ea9915..617df49 100644
--- a/lib/python/asterisk/voicemail.py
+++ b/lib/python/asterisk/voicemail.py
@@ -20,9 +20,9 @@
 import time
 import random
 
-from config import ConfigFile
-from test_case import TestCase
-from test_state import TestState, TestStateController, FailureTestState
+from .config import ConfigFile
+from .test_case import TestCase
+from .test_state import TestState, TestStateController, FailureTestState
 
 sys.path.append("lib/python")
 
@@ -232,7 +232,7 @@
         condition_name   The unique name of the condition
         value            The value to pass to the evaluation checker
         """
-        if condition_name in self._test_conditions.keys():
+        if condition_name in list(self._test_conditions.keys()):
             self._test_conditions[condition_name].evaluate(value)
 
     def get_test_condition(self, condition_name):
@@ -244,7 +244,7 @@
         Returns:
         True if the condition has passed; False otherwise
         """
-        if condition_name in self._test_conditions.keys():
+        if condition_name in list(self._test_conditions.keys()):
             return self._test_conditions[condition_name].current_state
         return False
 
@@ -463,7 +463,7 @@
                                         (self.test_parent_dir))
             shutil.copy(audio_file, msg_format_path)
 
-        if folder not in self.created_voicemails.keys():
+        if folder not in list(self.created_voicemails.keys()):
             self.created_voicemails[folder] = []
         self.created_voicemails[folder].append((msgnum, msg_id))
         return True
diff --git a/lib/python/pcap_listener.py b/lib/python/pcap_listener.py
index 4c27f91..4fdaac3 100644
--- a/lib/python/pcap_listener.py
+++ b/lib/python/pcap_listener.py
@@ -27,7 +27,7 @@
 
     def doRead(self):
         try:
-            pkt = self.pcap.next()
+            pkt = next(self.pcap)
         except PcapTimeout:
             return 0
 
diff --git a/lib/python/qm.py b/lib/python/qm.py
index bf63a41..bb89903 100644
--- a/lib/python/qm.py
+++ b/lib/python/qm.py
@@ -37,7 +37,8 @@
 >>> qm.get_function(qm.solve([0,1,2],[])[1])
 '((NOT B) OR (NOT A))'
 """
-
+from __future__ import absolute_import
+from asterisk.compat import xrange
 
 class QM:
   def __init__(self, variables):
diff --git a/lib/python/rlmi.py b/lib/python/rlmi.py
index 5e662a0..3838a64 100644
--- a/lib/python/rlmi.py
+++ b/lib/python/rlmi.py
@@ -17,13 +17,14 @@
 #   rlmi_schema_gen
 #
 
+from __future__ import absolute_import
 import sys
 import re as re_
 import base64
 import datetime as datetime_
 import warnings as warnings_
 from lxml import etree as etree_
-
+from asterisk.compat import iteritems
 
 Validate_simpletypes_ = True
 
@@ -343,7 +344,7 @@
             return None
         @classmethod
         def gds_reverse_node_mapping(cls, mapping):
-            return dict(((v, k) for k, v in mapping.iteritems()))
+            return dict(((v, k) for k, v in iteritems(mapping)))
 
 
 #
@@ -386,6 +387,8 @@
     "Escape markup chars, but do not modify CDATA sections."
     if not inStr:
         return ''
+    if sys.version_info >= (3, 0):
+        basestring = str
     s1 = (isinstance(inStr, basestring) and inStr or
           '%s' % inStr)
     s2 = ''
@@ -409,6 +412,8 @@
 
 
 def quote_attrib(inStr):
+    if sys.version_info >= (3, 0):
+        basestring = str
     s1 = (isinstance(inStr, basestring) and inStr or
           '%s' % inStr)
     s1 = s1.replace('&', '&')
diff --git a/lib/python/sip_message.py b/lib/python/sip_message.py
index 85cd9fd..85be13b 100644
--- a/lib/python/sip_message.py
+++ b/lib/python/sip_message.py
@@ -1,4 +1,5 @@
 #!/usr/bin/env python
+from __future__ import print_function
 import re
 
 
@@ -118,7 +119,7 @@
 def main():
     msg = """INVITE sip:123 at example.com SIP/2.0\r\nContact   : \tTerry Wilson\r\n   <terry at example.com>\r\nCall-ID:\r\n Whatever\r\nContact: New Contact\r\n\r\nData!!!!!"""
     sipmsg = SIPMessage(msg)
-    print sipmsg
+    print(sipmsg)
     if sipmsg.get_header('CoNtact') is None:
         return -1
     if len(sipmsg.get_header_all('contact')) != 2:
diff --git a/lib/python/asterisk/test_runner.py b/lib/python/test_runner.py
similarity index 97%
rename from lib/python/asterisk/test_runner.py
rename to lib/python/test_runner.py
index a097d02..a693dce 100755
--- a/lib/python/asterisk/test_runner.py
+++ b/lib/python/test_runner.py
@@ -17,6 +17,7 @@
 import logging.config
 import os
 import yaml
+import importlib
 
 from twisted.internet import reactor
 
@@ -25,7 +26,7 @@
 
 sys.path.append('lib/python')
 
-from version import AsteriskVersion
+from asterisk.version import AsteriskVersion
 
 
 class TestModuleFinder(object):
@@ -179,8 +180,14 @@
     if not len(module_name):
         LOGGER.error("No module specified: %s" % module_name)
         return None
+   
+    # Try load module from pkg asterisk
+    # If dont can load from test module
+    try:
+        module = importlib.import_module('asterisk.' + module_name)
+    except:
+        module = __import__(module_name)
 
-    module = __import__(module_name)
     for comp in parts[1:]:
         module = getattr(module, comp)
     return module
diff --git a/runtests.py b/runtests.py
index 7d324cf..be938d5 100755
--- a/runtests.py
+++ b/runtests.py
@@ -8,6 +8,7 @@
 the GNU General Public License Version 2.
 '''
 
+from __future__ import print_function
 import sys
 import os
 import errno
@@ -57,6 +58,7 @@
 from asterisk.test_config import TestConfig
 from mailer import send_email
 from asterisk import test_suite_utils
+from asterisk.compat import xrange
 
 TESTS_CONFIG = "tests.yaml"
 TEST_RESULTS = "asterisk-test-suite-report.xml"
@@ -105,8 +107,8 @@
         self.test_relpath = self.test_name[6:]
 
     def stdout_print(self, msg):
-        self.stdout += msg + "\n"
-        print msg
+        self.stdout += str(msg) + "\n"
+        print(msg)
 
     def run(self):
         self.passed = False
@@ -119,7 +121,7 @@
         ]
 
         if not os.path.exists(cmd[0]):
-            cmd = ["./lib/python/asterisk/test_runner.py",
+            cmd = ["./lib/python/test_runner.py",
                    "%s" % self.test_name]
         if os.path.exists(cmd[0]) and os.access(cmd[0], os.X_OK):
             self.stdout_print("Running %s ..." % cmd)
@@ -154,7 +156,7 @@
             if did_pass and not self.test_config.expect_pass:
                 self.stdout_print("Test passed but was expected to fail.")
             if not did_pass and not self.test_config.expect_pass:
-                print "Test failed as expected."
+                print("Test failed as expected.")
 
             self.passed = (did_pass == self.test_config.expect_pass)
             if abandon_test:
@@ -183,8 +185,8 @@
                     shutil.rmtree(absolute_dir)
                     os.remove(symlink_dir)
                 except:
-                    print "Unable to clean up directory for" \
-                          "test %s (non-fatal)" % self.test_name
+                    print(("Unable to clean up directory for"
+                          "test %s (non-fatal)" % self.test_name))
 
             self.__parse_run_output(self.stdout)
             if timedout:
@@ -196,12 +198,12 @@
             else:
                 status = 'failed'
             pass_str = 'Test %s %s\n' % (cmd, status)
-            print pass_str
+            print(pass_str)
             if self.options.syslog:
                 syslog.syslog(pass_str)
 
         else:
-            print "FAILED TO EXECUTE %s, it must exist and be executable" % cmd
+            print(("FAILED TO EXECUTE %s, it must exist and be executable" % cmd))
         self.time = time.time() - start_time
 
     def _check_for_core(self):
@@ -230,7 +232,7 @@
         debug_level = email_config.get('debug', 0)
 
         if not sender or len(recipients) == 0:
-            print "--email-on-crash requires sender and 1+ recipients"
+            print("--email-on-crash requires sender and 1+ recipients")
             return
 
         with open(dest_file_name, 'r') as bt_file:
@@ -248,12 +250,12 @@
             send_email(smtp_server, sender, recipients, message,
                        debug=debug_level)
         except Exception as exception:
-            print "Failed to send email\nError: {0}".format(exception)
+            print(("Failed to send email\nError: {0}".format(exception)))
 
     def _archive_core_dumps(self, core_dumps):
         for core in core_dumps:
             if not os.path.exists(core):
-                print "Unable to find core dump file %s, skipping" % core
+                print(("Unable to find core dump file %s, skipping" % core))
                 continue
             random_num = random.randint(0, 16000)
             dest_dir = "./logs/%s" % self.test_relpath
@@ -268,36 +270,36 @@
                        "-ex", "thread apply all bt",
                        "--batch",
                        "-c", core]
-            print "Running %s" % (" ".join(gdb_cmd),)
+            print(("Running %s" % (" ".join(gdb_cmd),)))
             try:
                 res = subprocess.call(gdb_cmd, stdout=dest_file, stderr=subprocess.STDOUT)
                 if res != 0:
-                    print "error analyzing core dump; gdb exited with %d" % res
+                    print(("error analyzing core dump; gdb exited with %d" % res))
                 # Copy the backtrace over to the logs
-                print "Archived backtrace: {0}".format(dest_file_name)
+                print(("Archived backtrace: {0}".format(dest_file_name)))
 
                 if self.options.email_on_crash:
                     self._email_crash_report(dest_file_name)
 
-            except OSError, ose:
-                print "OSError ([%d]: %s) occurred while executing %r" % \
-                    (ose.errno, ose.strerror, gdb_cmd)
+            except OSError(ose):
+                print(("OSError ([%d]: %s) occurred while executing %r" %
+                    (ose.errno, ose.strerror, gdb_cmd)))
             except:
-                print "Unknown exception occurred while executing %r" % (gdb_cmd,)
+                print(("Unknown exception occurred while executing %r" % (gdb_cmd,)))
             finally:
                 dest_file.close()
                 if self.options.keep_core:
                     try:
                         dst_core = os.path.join(dest_dir, "core_{0}".format(random_num))
                         shutil.copy(core, dst_core)
-                        print "Archived core file: {0}".format(dst_core)
+                        print(("Archived core file: {0}".format(dst_core)))
                     except Exception as e:
-                        print "Error occurred while copying core: {0}".format(e)
+                        print(("Error occurred while copying core: {0}".format(e)))
                 try:
                     os.unlink(core)
-                except OSError, e:
-                    print "Error removing core file: %s: " \
-                          "Beware of the stale core file in CWD!" % (e,)
+                except OSError(e):
+                    print(("Error removing core file: %s: "
+                          "Beware of the stale core file in CWD!" % (e,)))
 
     def _find_run_dirs(self):
         test_run_dir = os.path.join(Asterisk.test_suite_root,
@@ -380,7 +382,7 @@
                     res = subprocess.call(refcounter,
                                           stdout=dest_file,
                                           stderr=subprocess.STDOUT)
-                except Exception, e:
+                except Exception(e):
                     self.stdout_print("Exception occurred while processing REF_DEBUG")
                 finally:
                     dest_file.close()
@@ -408,10 +410,10 @@
                 srcfile = os.path.join(src_dir, filename)
                 if os.path.exists(srcfile):
                     hardlink_or_copy(srcfile, os.path.join(dest_dir, filename))
-            except Exception, e:
-                print "Exception occurred while archiving file '%s' to %s: %s" % (
+            except Exception(e):
+                print(("Exception occurred while archiving file '%s' to %s: %s" % (
                     srcfile, dest_dir, e
-                )
+                )))
 
     def _archive_logs(self):
         (run_num, run_dir, archive_dir) = self._find_run_dirs()
@@ -501,42 +503,42 @@
         tags.sort(key=str.lower)
         maxwidth = max(len(t) for t in tags)
 
-        print "Available test tags:"
+        print("Available test tags:")
         tags = chunks(tags, 3)
         for tag in tags:
-            print "\t%-*s     %-*s     %-*s" % (
+            print(("\t%-*s     %-*s     %-*s" % (
                 maxwidth, tag[0],
                 maxwidth, len(tag) > 1 and tag[1] or '',
-                maxwidth, len(tag) > 2 and tag[2] or '')
+                maxwidth, len(tag) > 2 and tag[2] or '')))
 
     def list_tests(self):
-        print "Configured tests:"
+        print("Configured tests:")
         i = 1
         for t in self.tests:
-            print "%.3d) %s" % (i, t.test_config.test_name)
-            print "      --> Summary: %s" % t.test_config.summary
+            print(("%.3d) %s" % (i, t.test_config.test_name)))
+            print(("      --> Summary: %s" % t.test_config.summary))
             if t.test_config.skip is not None:
-                print "      --> Skip: %s" % t.test_config.skip
-            print ("      --> Minimum Version: %s (%s)" %
+                print(("      --> Skip: %s" % t.test_config.skip))
+            print(("      --> Minimum Version: %s (%s)" %
                    (", ".join([str(v) for v in t.test_config.minversion]),
-                    t.test_config.minversion_check))
+                    t.test_config.minversion_check)))
             if t.test_config.maxversion is not None:
-                print ("      --> Maximum Version: %s (%s)" %
+                print(("      --> Maximum Version: %s (%s)" %
                        (", ".join([str(v) for v in t.test_config.maxversion]),
-                        t.test_config.maxversion_check))
+                        t.test_config.maxversion_check)))
             if t.test_config.features:
-                print "      --> Features:"
+                print("      --> Features:")
                 for feature_name in t.test_config.features:
-                    print "        --> %s: -- Met: %s" % \
-                        (feature_name, str(t.test_config.feature_check[feature_name]))
+                    print(("        --> %s: -- Met: %s" % \
+                        (feature_name, str(t.test_config.feature_check[feature_name]))))
             if t.test_config.tags:
-                print "      --> Tags: %s" % str(t.test_config.tags)
+                print(("      --> Tags: %s" % str(t.test_config.tags)))
             for d in t.test_config.deps:
                 if d.version:
-                    print "      --> Dependency: %s" % (d.name)
-                    print "        --> Version: %s -- Met: %s" % (d.version, str(d.met))
+                    print(("      --> Dependency: %s" % (d.name)))
+                    print(("        --> Version: %s -- Met: %s" % (d.version, str(d.met))))
                 else:
-                    print "      --> Dependency: %s -- Met: %s" % (d.name, str(d.met))
+                    print(("      --> Dependency: %s -- Met: %s" % (d.name, str(d.met))))
 
             i += 1
 
@@ -552,8 +554,8 @@
                     if excluded in t.test_name:
                         continue
             i += 1
-        print "Tests to run: %d,  Maximum test inactivity time: %d sec." % \
-            (i, (self.options.timeout / 1000))
+        print(("Tests to run: %d,  Maximum test inactivity time: %d sec." % \
+            (i, (self.options.timeout / 1000))))
 
         for t in self.tests:
             if abandon_test_suite:
@@ -561,28 +563,28 @@
 
             if t.can_run is False:
                 if t.test_config.skip is not None:
-                    print "--> %s ... skipped '%s'" % (t.test_name, t.test_config.skip)
+                    print(("--> %s ... skipped '%s'" % (t.test_name, t.test_config.skip)))
                     t.skipped_reason = t.test_config.skip
                     self.total_skipped += 1
                     continue
-                print "--> Cannot run test '%s'" % t.test_name
+                print(("--> Cannot run test '%s'" % t.test_name))
                 if t.test_config.forced_version is not None:
-                    print "--- --> Forced Asterisk Version: %s" % \
-                        (str(t.test_config.forced_version))
-                print ("--- --> Minimum Version: %s (%s)" %
+                    print(("--- --> Forced Asterisk Version: %s" %
+                        (str(t.test_config.forced_version))))
+                print(("--- --> Minimum Version: %s (%s)" %
                        (", ".join([str(v) for v in t.test_config.minversion]),
-                        t.test_config.minversion_check))
+                        t.test_config.minversion_check)))
                 if t.test_config.maxversion is not None:
-                    print ("--- --> Maximum Version: %s (%s)" %
+                    print(("--- --> Maximum Version: %s (%s)" %
                            (", ".join([str(v) for v in t.test_config.maxversion]),
-                            t.test_config.maxversion_check))
+                            t.test_config.maxversion_check)))
                 for f in t.test_config.features:
-                    print "--- --> Version Feature: %s - %s" % (
-                        f, str(t.test_config.feature_check[f]))
-                print "--- --> Tags: %s" % (t.test_config.tags)
+                    print(( "--- --> Version Feature: %s - %s" % (
+                        f, str(t.test_config.feature_check[f]))))
+                print(("--- --> Tags: %s" % (t.test_config.tags)))
                 for d in t.test_config.deps:
-                    print "--- --> Dependency: %s - %s" % (d.name, str(d.met))
-                print
+                    print(("--- --> Dependency: %s - %s" % (d.name, str(d.met))))
+                print()
                 self.total_skipped += 1
                 t.skipped_reason = "Failed dependency"
                 continue
@@ -590,14 +592,14 @@
                 exclude = False
                 for excluded in self.global_config.excluded_tests:
                     if excluded in t.test_name:
-                        print "--- ---> Excluded test: %s" % excluded
+                        print(("--- ---> Excluded test: %s" % excluded))
                         exclude = True
                 if exclude:
                     self.total_skipped += 1
                     continue
 
             running_str = "--> Running test '%s' ..." % t.test_name
-            print running_str
+            print(running_str)
             if self.options.syslog:
                 syslog.syslog(running_str)
 
@@ -605,16 +607,16 @@
                 t.passed = True
             else:
                 # Establish Preconditions
-                print "Making sure Asterisk isn't running ..."
+                print("Making sure Asterisk isn't running ...")
                 if os.system("if pidof asterisk >/dev/null; then "
                              "killall -9 asterisk >/dev/null 2>&1; "
                              "sleep 1; ! pidof asterisk >/dev/null; fi"):
-                    print "Could not kill asterisk."
-                print "Making sure SIPp isn't running..."
+                    print("Could not kill asterisk.")
+                print("Making sure SIPp isn't running...")
                 if os.system("if pidof sipp >/dev/null; then "
                              "killall -9 sipp >/dev/null 2>&1; "
                              "sleep 1; ! pidof sipp >/dev/null; fi"):
-                    print "Could not kill sipp."
+                    print("Could not kill sipp.")
                 # XXX TODO Hard coded path, gross.
                 os.system("rm -f /var/run/asterisk/asterisk.ctl")
                 os.system("rm -f /var/run/asterisk/asterisk.pid")
@@ -701,10 +703,10 @@
     except IOError:
         # Ignore errors for the optional tests/custom folder.
         if path != "tests/custom/tests.yaml":
-            print "Failed to open %s" % path
+            print(("Failed to open %s" % path))
         return None
     except:
-        print "Unexpected error: %s" % sys.exc_info()[0]
+        print(("Unexpected error: %s" % sys.exc_info()[0]))
         return None
 
     config = yaml.load(f)
@@ -720,7 +722,7 @@
     """
     global abandon_test_suite
 
-    print "SIGUSR1 received; stopping test suite after current test..."
+    print("SIGUSR1 received; stopping test suite after current test...")
     abandon_test_suite = True
 
 
@@ -733,7 +735,7 @@
     global abandon_test
     global abandon_test_suite
 
-    print "SIGTREM received; abandoning current test and stopping..."
+    print("SIGTREM received; abandoning current test and stopping...")
     abandon_test = True
     abandon_test_suite = True
 
@@ -801,7 +803,6 @@
     signal.signal(signal.SIGTERM, handle_term)
 
     ast_version = AsteriskVersion(default=options.version)
-
     if options.list_tests or options.list_tags:
         test_suite = TestSuite(ast_version, options)
 
@@ -823,8 +824,8 @@
 
     if options.valgrind:
         if not ET:
-            print "python lxml module not loaded, text summaries " \
-                  "from valgrind will not be produced.\n"
+            print("python lxml module not loaded, text summaries "
+                  "from valgrind will not be produced.\n")
         os.environ["VALGRIND_ENABLE"] = "true"
 
     dom = xml.dom.getDOMImplementation()
@@ -840,7 +841,7 @@
 
         running_str = "Running tests for Asterisk {0} (run {1})...\n".format(
             str(ast_version).strip('\n'), iteration + 1)
-        print running_str
+        print(running_str)
         if options.syslog:
             syslog.syslog(running_str)
 
@@ -849,22 +850,22 @@
 
         # If exactly one test was requested, then skip the summary.
         if len(test_suite.tests) != 1:
-            print "\n=== TEST RESULTS ===\n"
-            print "PATH: %s\n" % os.getenv("PATH")
+            print("\n=== TEST RESULTS ===\n")
+            print(("PATH: %s\n" % os.getenv("PATH")))
             for t in test_suite.tests:
                 sys.stdout.write("--> %s --- " % t.test_name)
                 if t.did_run is False:
-                    print "SKIPPED"
+                    print("SKIPPED")
                     for d in t.test_config.deps:
-                        print "      --> Dependency: %s -- Met: %s" % (d.name, str(d.met))
+                        print(("      --> Dependency: %s -- Met: %s" % (d.name, str(d.met))))
                     if options.tags:
                         for t in t.test_config.tags:
-                            print "      --> Tag: %s -- Met: %s" % (t, str(t in options.tags))
+                            print(("      --> Tag: %s -- Met: %s" % (t, str(t in options.tags))))
                     continue
                 if t.passed is True:
-                    print "PASSED"
+                    print("PASSED")
                 else:
-                    print "FAILED"
+                    print("FAILED")
 
         iteration += 1
 
@@ -875,11 +876,11 @@
         with open(TEST_RESULTS, "w") as f:
             doc.writexml(f, addindent="  ", newl="\n", encoding="utf-8")
     except IOError:
-        print "Failed to open test results output file: %s" % TEST_RESULTS
+        print(("Failed to open test results output file: %s" % TEST_RESULTS))
     except:
-        print "Unexpected error: %s" % sys.exc_info()[0]
-    print "\n"
-    print doc.toprettyxml("  ", encoding="utf-8")
+        print(("Unexpected error: %s" % sys.exc_info()[0]))
+    print("\n")
+    print((doc.toprettyxml("  ", encoding="utf-8")))
 
     if options.syslog:
         syslog.syslog("All tests concluded")
@@ -900,7 +901,7 @@
 
     try:
         os.link(source, destination)
-    except OSError, e:
+    except OSError(e):
         # Different partitions can cause hard links to fail (error 18),
         # if there's a different error, bail out immediately.
         if e.args[0] != errno.EXDEV:
diff --git a/tests/cdr/cdr-tests.py b/tests/cdr/cdr-tests.py
index a284673..fba18d4 100644
--- a/tests/cdr/cdr-tests.py
+++ b/tests/cdr/cdr-tests.py
@@ -13,8 +13,8 @@
 import time
 
 sys.path.append("lib/python")
-from cdr import CDRModule
-from cdr import AsteriskCSVCDR
+from asterisk.cdr import CDRModule
+from asterisk.cdr import AsteriskCSVCDR
 
 LOGGER = logging.getLogger(__name__)
 
@@ -171,7 +171,7 @@
                  "cdrtest_local"))
 
         LOGGER.debug('Checking start/end times for forked entries')
-        for i in range(len(self.entries_to_check) - 1):
+        for i in list(range(len(self.entries_to_check) - 1)):
             end = time.strptime(cdr1[self.entries_to_check[i]].end,
                 "%Y-%m-%d %H:%M:%S")
             beg = time.strptime(cdr1[self.entries_to_check[i + 1]].start,
diff --git a/tests/cdr/sqlite3/cdr_sqlite3.py b/tests/cdr/sqlite3/cdr_sqlite3.py
index 1def05e..ff8e672 100644
--- a/tests/cdr/sqlite3/cdr_sqlite3.py
+++ b/tests/cdr/sqlite3/cdr_sqlite3.py
@@ -14,8 +14,7 @@
 import re
 
 sys.path.append("lib/python")
-from config import ConfigFile
-
+from asterisk.config import ConfigFile
 LOGGER = logging.getLogger(__name__)
 
 class CDRSQLite3Verifier(object):
diff --git a/tox.ini b/tox.ini
index e5cc8ca..f6a3377 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,2 +1,5 @@
+[tox]
+skipsdist = True
+
 [pep8]
 max-line-length=90
diff --git a/usage.py b/usage.py
index 03a9f35..56b9a77 100755
--- a/usage.py
+++ b/usage.py
@@ -8,6 +8,7 @@
 the GNU General Public License Version 2.
 '''
 
+from __future__ import print_function
 import sys
 import yaml
 
@@ -80,7 +81,7 @@
     def occurances(self, **kwargs):
         match = []
         for test in self.tests:
-            for key, value in kwargs.iteritems():
+            for key, value in kwargs.items():
                 if value in getattr(test, key):
                     match.append(test)
                     continue
@@ -88,14 +89,14 @@
         return len(match)
 
     def results_for(self, key):
-        print key.title() + ":"
+        print((key.title() + ":"))
         things = self.unique(key)
         width = max(len(t) for t in things)
         results = [(self.occurances(**{key: t}), t) for t in things]
         results.sort(key=lambda tup: tup[0], reverse=True)
         for (count, name) in results:
-            print "\t%-*s %5d" % (width, name, count)
-        print ""
+            print(("\t%-*s %5d" % (width, name, count)))
+        print("")
 
 
 def load_yaml_config(path):
@@ -105,10 +106,10 @@
     except IOError:
         # Ignore errors for the optional tests/custom folder.
         if path != "tests/custom/tests.yaml":
-            print "Failed to open %s" % path
+            print(("Failed to open %s" % path))
         return None
     except:
-        print "Unexpected error: %s" % sys.exc_info()[0]
+        print(("Unexpected error: %s" % sys.exc_info()[0]))
         return None
 
     config = yaml.load(f)
@@ -118,11 +119,11 @@
 
 
 def main(argv=None):
-    print "Testsuite Module Usage and Coverage Report"
-    print ""
+    print("Testsuite Module Usage and Coverage Report")
+    print("")
     test_suite = TestSuite()
-    print "Number of tests:", len(test_suite.tests)
-    print ""
+    print("Number of tests:", len(test_suite.tests))
+    print("")
     test_suite.results_for('tags')
     test_suite.results_for('test_objects')
     test_suite.results_for('test_modules')

-- 
To view, visit https://gerrit.asterisk.org/4074
To unsubscribe, visit https://gerrit.asterisk.org/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I58546fd8c0cff97f73aa1e80ebb85851102008fb
Gerrit-PatchSet: 1
Gerrit-Project: testsuite
Gerrit-Branch: master
Gerrit-Owner: Rodrigo Ramirez Norambuena <a at rodrigoramirez.com>



More information about the asterisk-code-review mailing list