Skip to content

Commit e384129

Browse files
committed
Begin work on python3 compatability.
* Use explicit relative import syntax. * Correct syntax for print and except. * Fix incorrect usage of IOError in astcdr.py - IOError is not a tuple. * Use items, values instead of iteritems, itervalues. * Use OrderedDict from collections if available (python 2.7+). * Do not unpack tuples from argument list, save tuple to variable then unpack within code. * Use exception handler to import Python3 version of urlencode if the Python2 version fails to import. * Remove unused import unittest from opensslversion. * Move executable tests to separate files to resolve issues with relative imports: - buildoptions - cdr - cel - channel_test_condition - config - lock_test_condition - sip_dialog_test_condition (disabled) - sippversion * Modify utils_socket test to use new harness_shared module. * Remove unused executable flag and shebang from phones.py. * Update self_test script to run tests from new location. * Remove ability to use config.py to display a config file. * Allow PYTHON environmental variable to tell run-local to use a specific python binary. * Execute test_runner.py using python -m syntax. This avoids conflicts between needing to use relative imports for modules but not being allowed to use them in executable scripts. * Encode self.realbase for use with md5 function. * Remove executable flag and shebang from test_runner.py. Remove redundant appending of sys.path. * Modify output for running test and status to print the name of the test instead of the command used to run the test. * Use key= sorter argument to compare sizes of tmp filesystems. This may cause us to use /var/tmp instead of /tmp even if /var/tmp has only a few kilobytes more space but python3 does not support cmp= argument to sorter. * Decode binary to utf-8 - CLI output - runtest.py subprocess output - XML printed to stdout - SyncAMI data - SIPp runtime and version output * Update test_runner.load_and_parse_module to check for the module in lib/python/asterisk, make required adjustments if found. * Update __strip_illegal_xml_chars to work with unicode version of str.translate which does not support second argument. * Update import statements from all *.py modules within tests/ to use absolute names for asterisk modules. This is required for python2 to continue functioning with the new way that test_runner must be executed. No attempt has been made to fix python3 compatibility issues in these modules. Some tests may pass using python3. Doing so requires using an updated starpy and no python sources within tests/ have been updated. Unit tests from lib/python can be run against python2 and python3 using ./self_test. The goal of this is for all tests to continue functioning using python2. Some tests may work under python3 but individual tests will need to be addressed separately. For tests which do not contain a run-test script the version of python used to execute ./runtests.py will be used: ./runtests.py -t tests/manager/originate python2 ./runtests.py -t tests/manager/originate python3 ./runtests.py -t tests/manager/originate These commands will be python, python2 and python3 respectively. The first example where no interpreter is specified uses the shebang from ./runtests.py. The interpreter used by ./run-local can be controlled by setting PYTHON environmental variable: PYTHON=python3 ./run-local run -t tests/manager/originate Change-Id: If76c2d3e11e4ab4552d0df7841287c8bb2de7918
1 parent 3cec596 commit e384129

File tree

85 files changed

+1930
-1861
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

85 files changed

+1930
-1861
lines changed

Diff for: lib/python/asterisk/ami.py

+5-5
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@
1313
import logging
1414
import re
1515
import json
16-
from pluggable_registry import PLUGGABLE_EVENT_REGISTRY,\
16+
from .test_runner import load_and_parse_module
17+
from .pluggable_registry import PLUGGABLE_EVENT_REGISTRY,\
1718
PLUGGABLE_ACTION_REGISTRY, var_replace
1819

1920
LOGGER = logging.getLogger(__name__)
@@ -361,7 +362,7 @@ def __init__(self, requirements):
361362
lower_key = key.lower()
362363
if lower_key == 'extra':
363364
value = dict((key.lower(), value)
364-
for key, value in value.iteritems())
365+
for key, value in value.items())
365366
self.requirements[lower_key] = value
366367
self.orderings = requirements.get('partialorder') or []
367368
self.named_id = requirements.get('id')
@@ -529,8 +530,7 @@ def __init__(self, instance_config, test_object):
529530

530531
def event_callback(self, ami, event):
531532
"""Callback called when an event is received from AMI"""
532-
callback_module = __import__(self.callback_module)
533-
method = getattr(callback_module, self.callback_method)
533+
method = load_and_parse_module(self.callback_module + '.' + self.callback_method)
534534
self.passed = method(ami, event)
535535
if self.passed is None:
536536
LOGGER.error("Callback %s.%s returned None instead of a boolean",
@@ -828,7 +828,7 @@ def __init__(self, test_object, triggered_callback, config):
828828

829829
def replace_ami_vars(mydict, values):
830830
outdict = {}
831-
for key, value in mydict.iteritems():
831+
for key, value in mydict.items():
832832
outdict[key] = var_replace(value, values)
833833

834834
return outdict

Diff for: lib/python/asterisk/apptest.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@
1818
from twisted.internet import reactor, defer
1919

2020
sys.path.append("lib/python")
21-
from test_case import TestCase
22-
from ami import AMIEventInstance
21+
from .test_case import TestCase
22+
from .ami import AMIEventInstance
2323

2424
LOGGER = logging.getLogger(__name__)
2525

@@ -165,7 +165,7 @@ def _is_scenario_done(self):
165165
A Scenario is considered done if all results have been met and
166166
all expected actions have been executed.
167167
"""
168-
return (all(self._expected_results.itervalues()) and
168+
return (all(self._expected_results.values()) and
169169
all(i.ran_actions or i.unexpected
170170
for i in self._event_instances))
171171

Diff for: lib/python/asterisk/ari.py

+12-9
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,16 @@
1212
import re
1313
import requests
1414
import traceback
15-
import urllib
15+
try:
16+
from urllib.parse import urlencode
17+
except:
18+
from urllib import urlencode
1619

17-
from test_case import TestCase
18-
from pluggable_registry import PLUGGABLE_EVENT_REGISTRY,\
20+
from .test_case import TestCase
21+
from .test_runner import load_and_parse_module
22+
from .pluggable_registry import PLUGGABLE_EVENT_REGISTRY,\
1923
PLUGGABLE_ACTION_REGISTRY, var_replace
20-
from test_suite_utils import all_match
24+
from .test_suite_utils import all_match
2125
from twisted.internet import reactor
2226
try:
2327
from autobahn.websocket import WebSocketClientFactory, \
@@ -332,7 +336,7 @@ def __init__(self, receiver, host, apps, userpass, port=DEFAULT_PORT,
332336
"""
333337
url = "ws://%s:%d/ari/events?%s" % \
334338
(host, port,
335-
urllib.urlencode({'app': apps, 'api_key': '%s:%s' % userpass}))
339+
urlencode({'app': apps, 'api_key': '%s:%s' % userpass}))
336340
if subscribe_all:
337341
url += '&subscribeAll=true'
338342
LOGGER.info("WebSocketClientFactory(url=%s)", url)
@@ -560,7 +564,7 @@ def send(self, values):
560564
url = self.ari.build_url(uri)
561565
requests_method = getattr(requests, self.method)
562566
params = dict((key, var_replace(val, values))
563-
for key, val in self.params.iteritems())
567+
for key, val in self.params.items())
564568

565569
response = requests_method(
566570
url,
@@ -580,7 +584,7 @@ def send(self, values):
580584
response.status_code, response.text)
581585
return False
582586
else:
583-
if response.status_code / 100 != 2:
587+
if response.status_code // 100 != 2:
584588
LOGGER.error('sent %s %s %s response %d %s',
585589
self.method, self.uri, self.params,
586590
response.status_code, response.text)
@@ -606,8 +610,7 @@ def __init__(self, ari, instance_config, test_object):
606610

607611
callback = self.instance_config.get('callback')
608612
if callback:
609-
module = __import__(callback['module'])
610-
self.callback = getattr(module, callback['method'])
613+
self.callback = load_and_parse_module(callback['module'] + '.' + callback['method'])
611614
else:
612615
# No callback; just use a no-op
613616
self.callback = lambda *args, **kwargs: True

Diff for: lib/python/asterisk/astconfigparser.py

+7-8
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@
88
import re
99
import itertools
1010

11-
from astdicts import OrderedDict
12-
from astdicts import MultiOrderedDict
11+
from .astdicts import OrderedDict, MultiOrderedDict
1312

1413

1514
def merge_values(left, right, key):
@@ -270,11 +269,11 @@ def found(d):
270269

271270
def write_dicts(config_file, mdicts):
272271
"""Write the contents of the mdicts to the specified config file"""
273-
for section, sect_list in mdicts.iteritems():
272+
for section, sect_list in mdicts.items():
274273
# every section contains a list of dictionaries
275274
for sect in sect_list:
276275
config_file.write("[%s]\n" % section)
277-
for key, val_list in sect.iteritems():
276+
for key, val_list in sect.items():
278277
# every value is also a list
279278
for v in val_list:
280279
key_val = key
@@ -355,7 +354,7 @@ def get_sections(self, key, attr='_sections', searched=None):
355354
if self._includes:
356355
res.extend(list(itertools.chain(*[
357356
incl.get_sections(key, attr, searched)
358-
for incl in self._includes.itervalues()])))
357+
for incl in self._includes.values()])))
359358
if self._parent:
360359
res += self._parent.get_sections(key, attr, searched)
361360
return res
@@ -445,7 +444,7 @@ def read(self, filename, sect=None):
445444
with open(filename, 'rt') as config_file:
446445
self._read(config_file, sect)
447446
except IOError:
448-
print "Could not open file ", filename, " for reading"
447+
print("Could not open file ", filename, " for reading")
449448

450449
def _read(self, config_file, sect):
451450
"""Parse configuration information from the config_file"""
@@ -478,7 +477,7 @@ def _read(self, config_file, sect):
478477
def write(self, config_file):
479478
"""Write configuration information out to a file"""
480479
try:
481-
for key, val in self._includes.iteritems():
480+
for key, val in self._includes.items():
482481
val.write(key)
483482
config_file.write('#include "%s"\n' % key)
484483

@@ -490,4 +489,4 @@ def write(self, config_file):
490489
with open(config_file, 'wt') as fp:
491490
self.write(fp)
492491
except IOError:
493-
print "Could not open file ", config_file, " for writing"
492+
print("Could not open file ", config_file, " for writing")

Diff for: lib/python/asterisk/astcsv.py

+16-17
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
#!/usr/bin/env python
21
"""Asterisk CSV-based testing
32
43
This module implements the basic CSV testing backend for things like
@@ -11,7 +10,6 @@
1110
the GNU General Public License Version 2.
1211
"""
1312

14-
import unittest
1513
import sys
1614
import csv
1715
import re
@@ -57,10 +55,10 @@ def match(self, other, silent=False, exact=(False, False)):
5755
else:
5856
cmp_fn = (lambda x, y: str(x).lower() == str(y).lower())
5957

60-
for key, value in self.iteritems():
58+
for key, value in self.items():
6159
if None not in (value, other.get(key)) and not cmp_fn(value, other.get(key)):
6260
if not silent:
63-
LOGGER.warn("CSV MATCH FAILED, Expected: %s: '%s' "
61+
LOGGER.warning("CSV MATCH FAILED, Expected: %s: '%s' "
6462
"Got: %s: '%s'" % (key, value, key,
6563
other.get(key)))
6664
return False
@@ -70,9 +68,9 @@ def get(self, key):
7068
"""Retrieve a value from the specified column key"""
7169
return self.__columns.get(key)
7270

73-
def iteritems(self):
71+
def items(self):
7472
"""Iterate over the values in the columns"""
75-
return self.__columns.iteritems()
73+
return self.__columns.items()
7674

7775
def __str__(self):
7876
return ",".join(["\"%s\"" % (self.__dict__[x]) for x in self.__fields])
@@ -94,12 +92,14 @@ def __init__(self, fname=None, records=None, fields=None, row_factory=None):
9492
self.__records = []
9593

9694
csvreader = None
95+
csvfile = None
9796

9897
try:
99-
csvreader = csv.DictReader(open(self.filename, "r"), fields, ",")
100-
except IOError as (errno, strerror):
98+
csvfile = open(self.filename, "r")
99+
csvreader = csv.DictReader(csvfile, fields, ",")
100+
except IOError as e:
101101
LOGGER.error("IOError %d[%s] while opening file '%s'" %
102-
(errno, strerror, self.filename))
102+
(e.errno, e.strerror, self.filename))
103103
except:
104104
LOGGER.error("Unexpected error: %s" % (sys.exc_info()[0]))
105105

@@ -111,6 +111,8 @@ def __init__(self, fname=None, records=None, fields=None, row_factory=None):
111111
record = self.row_factory(**row)
112112
self.__records.append(record)
113113

114+
csvfile.close()
115+
114116
def __len__(self):
115117
return len(self.__records)
116118

@@ -125,7 +127,7 @@ def match(self, other, partial=False):
125127
each record"""
126128

127129
if not partial and (len(self) != len(other)):
128-
LOGGER.warn("CSV MATCH FAILED, different number of records, "
130+
LOGGER.warning("CSV MATCH FAILED, different number of records, "
129131
"self=%d and other=%d" % (len(self), len(other)))
130132
return False
131133

@@ -147,7 +149,7 @@ def match_order(list_a, list_b, cmp_func):
147149
size = len(list_a)
148150

149151
# attempt two orderings: forward and reversed
150-
guess_orders = (range(size), list(reversed(range(size))))
152+
guess_orders = (list(range(size)), list(reversed(range(size))))
151153
found_orders = []
152154

153155
for guess_order in guess_orders:
@@ -185,15 +187,15 @@ def match_order(list_a, list_b, cmp_func):
185187
# have it complain immediately.
186188
for i, item in enumerate(self):
187189
if not item.match(other[i], exact=exactness):
188-
LOGGER.warn("Failed to match entry %d" % (i,))
190+
LOGGER.warning("Failed to match entry %d" % (i,))
189191
return False
190192
assert False
191193

192194
elif len(matches) == 1:
193195
pass # joy!
194196

195197
elif len(matches) > 1:
196-
LOGGER.warn("More than one CSV permutation results in success")
198+
LOGGER.warning("More than one CSV permutation results in success")
197199

198200
return True
199201

@@ -205,9 +207,6 @@ def empty(self):
205207
try:
206208
open(self.filename, "w").close()
207209
except:
208-
LOGGER.warn("Unable to empty CSV file %s" % (self.filename))
209-
210-
if __name__ == '__main__':
211-
unittest.main()
210+
LOGGER.warning("Unable to empty CSV file %s" % (self.filename))
212211

213212
# vim:sw=4:ts=4:expandtab:textwidth=79

0 commit comments

Comments
 (0)