Skip to content

Commit b6cb560

Browse files
wallacbeKenadia
authored andcommitted
Merge python3 into master (#679)
* python3 WIP (#674) * python3: parens around single-line print statements * python3: subprocess.check_output() => subprocess.getoutput() * python3: 2to3 * python3: don't use relative protos, generate from root and include relative to root. Delete string.decode(), will fix with future unicode_literal later. * Python3: PhaseExecutionOutcome has __new__ and not __init__ since named tuples are immutable * Python3: Initialize TestRecord.start_time_millis to 0. In PlugsTest.test_initialize, only compare the knowable values of the plug_manager dictionary. * Python3: self.join fails with float_info.max, replace with 5 seconds (it seems to be a placeholder value). Encode the test_attachment string as utf-8 * Python3: CodeInfo must derive from HashableRecord since it's held in a dict. * Python3: Don't utf8-encode the unit suffixes * Python3: explicitly encode value_binary fields as utf-8 * Python3: BytesIO instead of StringIO in tests. * Python3: Store attachments as unicode strings containing base64-encoded payloads (used to be byte strings of b64 payloads). Use a StringIO for the test that needs it. * Python3: Remove 2to3 unnecessary double-parentheses around print calls. * Python3: Point Travis at Python 3.6 * Python3: Require protobuf 3.4.0, exploring Travis failures * Python3: Require protobuf package >3, explicitly download + unpack protoc > 3. From Brandon Wallace (wallacbe) * python3 updates to allow examples to run * updates to allow examples to run in python27 * enable pyhon2.7 in travis * add __next__method * update test_json * update long in data test * Update raises regex * travis updates * remove python3 checks * updates based on comments * try to address feedback from fahhem * update test_executor timeout * address additional code review * use hashable record
1 parent 109771f commit b6cb560

Some content is hidden

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

56 files changed

+297
-250
lines changed

.travis.yml

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
11
language: python
2-
python:
3-
- '2.7'
2+
matrix:
3+
include:
4+
- python: 2.7
5+
env: TOXENV=py27
6+
- python: 3.6
7+
env: TOXENV=py36
48
addons:
59
apt:
610
packages:
11+
- python3
12+
- python3-pip
713
- swig
814
- libusb-1.0-0-dev
915
- libprotobuf-dev
@@ -12,8 +18,6 @@ cache:
1218
apt: true
1319
directories:
1420
- $HOME/.cache/pip
15-
env:
16-
- TOXENV=py27
1721
install:
1822
- pip install tox coveralls
1923
- pip install -e .

CONTRIBUTING.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -213,8 +213,8 @@ accordingly.
213213
git clone https://github.com/google/openhtf.git
214214

215215
# Install system-level third-party dependencies.
216-
sudo apt-get install python-pip swig libssl-dev python-dev libffi-dev \
217-
protobuf-compiler libprotobuf-dev
216+
sudo apt-get install python-pip swig libssl-dev python-dev python3-dev \
217+
libffi-dev protobuf-compiler libprotobuf-dev
218218

219219
# Make sure pip is up-to-date.
220220
sudo pip install --upgrade pip

bin/units_from_xls.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -158,10 +158,10 @@ def __call__(self, name_or_suffix):
158158
'15': 'FIFTEEN',
159159
'30': 'THIRTY',
160160
'\\': '_',
161-
unichr(160): '_',
162-
unichr(176): 'DEG_',
163-
unichr(186): 'DEG_',
164-
unichr(8211): '_',
161+
chr(160): '_',
162+
chr(176): 'DEG_',
163+
chr(186): 'DEG_',
164+
chr(8211): '_',
165165
}
166166

167167

@@ -182,7 +182,7 @@ def main():
182182
args = parser.parse_args()
183183

184184
if not os.path.exists(args.xlsfile):
185-
print 'Unable to locate the file "%s".' % args.xlsfile
185+
print('Unable to locate the file "%s".' % args.xlsfile)
186186
parser.print_help()
187187
sys.exit()
188188

@@ -217,7 +217,7 @@ def unit_defs_from_sheet(sheet, column_names):
217217
rows = sheet.get_rows()
218218

219219
# Find the indices for the columns we care about.
220-
for idx, cell in enumerate(rows.next()):
220+
for idx, cell in enumerate(next(rows)):
221221
if cell.value in column_names:
222222
col_indices[cell.value] = idx
223223

@@ -245,7 +245,7 @@ def unit_key_from_name(name):
245245
"""Return a legal python name for the given name for use as a unit key."""
246246
result = name
247247

248-
for old, new in UNIT_KEY_REPLACEMENTS.iteritems():
248+
for old, new in UNIT_KEY_REPLACEMENTS.items():
249249
result = result.replace(old, new)
250250

251251
# Collapse redundant underscores and convert to uppercase.

contrib/poll_stations.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,10 @@
3030
3131
"""
3232

33+
from __future__ import print_function
3334
import logging
3435
import os
35-
import Queue
36+
import queue
3637
import socket
3738
import sys
3839
import threading
@@ -111,7 +112,7 @@ class StationList(object):
111112
"""
112113

113114
def __init__(self):
114-
self.update_queue = Queue.Queue()
115+
self.update_queue = queue.Queue()
115116
self.stations = set()
116117
# Really, these threads should be tracked on a per-station basis, because
117118
# two stations *could* have RemoteTest instances that would compare equal
@@ -168,7 +169,7 @@ def watch_test(self, remote_test):
168169
def print_station(self, station):
169170
print(station)
170171
try:
171-
for remote_test in station.tests.itervalues():
172+
for remote_test in station.tests.values():
172173
# Trigger an update of the local history cache and state.
173174
remote_test.state
174175
remote_test.history
@@ -182,7 +183,7 @@ def print_station(self, station):
182183
self.update_threads[remote_test] = update_thread
183184
except socket.error as e:
184185
print(' |-- Connection Error: %s' % e)
185-
print
186+
print()
186187

187188
def check_for_stations(self):
188189
"""Discover for new stations, doesn't remove any stations."""
@@ -199,7 +200,7 @@ def mainloop(self):
199200
station_list.check_for_stations()
200201
try:
201202
self.update(self.update_queue.get(timeout=5))
202-
except Queue.Empty:
203+
except queue.Empty:
203204
pass
204205

205206

examples/all_the_things.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
from openhtf.output import callbacks
2929
from openhtf.output.callbacks import json_factory
3030

31-
import example_plugs
31+
from examples import example_plugs
3232

3333

3434
@htf.plug(example=example_plugs.ExamplePlug)
@@ -107,7 +107,7 @@ def measures_with_args(test, min, max):
107107

108108

109109
def attachments(test):
110-
test.attach('test_attachment', 'This is test attachment data.')
110+
test.attach('test_attachment', 'This is test attachment data.'.encode('utf-8'))
111111
test.attach_from_file(
112112
os.path.join(os.path.dirname(__file__), 'example_attachment.txt'))
113113

examples/repeat.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
phase can be limited specifying a PhaseOptions.repeat_limit.
2828
"""
2929

30+
from __future__ import print_function
3031
import openhtf
3132
import openhtf.plugs as plugs
3233

@@ -40,7 +41,7 @@ def __init__(self):
4041
def run(self):
4142
"""Increments counter and raises an exception for first two runs."""
4243
self.count += 1
43-
print 'FailTwicePlug: Run number %s' % (self.count)
44+
print('FailTwicePlug: Run number %s' % (self.count))
4445
if self.count < 3:
4546
raise RuntimeError('Fails a couple times')
4647

@@ -56,7 +57,7 @@ def __init__(self):
5657
def run(self):
5758
"""Increments counter and returns False indicating failure"""
5859
self.count += 1
59-
print "FailAlwaysPlug: Run number %s" % (self.count)
60+
print("FailAlwaysPlug: Run number %s" % (self.count))
6061

6162
return False
6263

@@ -70,10 +71,10 @@ def phase_repeat(test, test_plug):
7071
test_plug.run()
7172

7273
except:
73-
print "Error in phase_repeat, will retry"
74+
print("Error in phase_repeat, will retry")
7475
return openhtf.PhaseResult.REPEAT
7576

76-
print "Completed phase_repeat"
77+
print("Completed phase_repeat")
7778

7879

7980
# This phase demonstrates repeating a phase based upon a result returned from a
@@ -86,7 +87,7 @@ def phase_repeat_with_limit(test, test_plug):
8687
result = test_plug.run()
8788

8889
if not result:
89-
print "Invalid result in phase_repeat_with_limit, will retry"
90+
print("Invalid result in phase_repeat_with_limit, will retry")
9091
return openhtf.PhaseResult.REPEAT
9192

9293
if __name__ == '__main__':

examples/with_plugs.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
end up with the 4 phases you want.
2626
"""
2727

28+
from __future__ import print_function
2829
import subprocess
2930
import time
3031

@@ -52,7 +53,7 @@ def _get_command(self, count):
5253

5354
def run(self, count):
5455
command = self._get_command(count)
55-
print "running: %s" % ' '.join(command)
56+
print("running: %s" % ' '.join(command))
5657
return subprocess.call(command)
5758

5859

openhtf/__init__.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ def configure(self, **kwargs):
192192
# side effects.
193193
create_arg_parser(add_help=True).parse_known_args()
194194
logs.setup_logger()
195-
for key, value in kwargs.iteritems():
195+
for key, value in kwargs.items():
196196
setattr(self._test_options, key, value)
197197

198198
@classmethod
@@ -390,7 +390,7 @@ def format_strings(self, **kwargs):
390390
self, name=util.format_string(self.name, kwargs))
391391

392392
def update(self, **kwargs):
393-
for key, value in kwargs.iteritems():
393+
for key, value in kwargs.items():
394394
if key not in self.__slots__:
395395
raise AttributeError('Type %s does not have attribute %s' % (
396396
type(self).__name__, key))
@@ -481,7 +481,7 @@ def with_plugs(self, **subplugs):
481481
plugs_by_name = {plug.name: plug for plug in self.plugs}
482482
new_plugs = dict(plugs_by_name)
483483

484-
for name, sub_class in subplugs.iteritems():
484+
for name, sub_class in subplugs.items():
485485
original_plug = plugs_by_name.get(name)
486486
accept_substitute = True
487487
if original_plug is None:
@@ -503,7 +503,7 @@ def with_plugs(self, **subplugs):
503503

504504
return mutablerecords.CopyRecord(
505505
self,
506-
plugs=new_plugs.values(),
506+
plugs=list(new_plugs.values()),
507507
options=self.options.format_strings(**subplugs),
508508
measurements=[m.with_args(**subplugs) for m in self.measurements])
509509

openhtf/core/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
from test_executor import TestExecutionError, TestStopError, TestExecutor
1+
from .test_executor import TestExecutionError, TestStopError, TestExecutor

openhtf/core/measurements.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -325,7 +325,7 @@ def is_value_set(self):
325325

326326
def __iter__(self): # pylint: disable=invalid-name
327327
"""Iterate over items, allows easy conversion to a dict."""
328-
return self.value_dict.iteritems()
328+
return iter(self.value_dict.items())
329329

330330
def __setitem__(self, coordinates, value): # pylint: disable=invalid-name
331331
coordinates_len = len(coordinates) if hasattr(coordinates, '__len__') else 1
@@ -366,7 +366,7 @@ def value(self):
366366
if not self.is_value_set:
367367
raise MeasurementNotSetError('Measurement not yet set', self.name)
368368
return [dimensions + (value,) for dimensions, value in
369-
self.value_dict.iteritems()]
369+
self.value_dict.items()]
370370

371371

372372
class Collection(mutablerecords.Record('Collection', ['_measurements'])):
@@ -421,7 +421,7 @@ def _assert_valid_key(self, name):
421421
def __iter__(self): # pylint: disable=invalid-name
422422
"""Extract each MeasurementValue's value."""
423423
return ((key, meas.measured_value.value)
424-
for key, meas in self._measurements.iteritems())
424+
for key, meas in self._measurements.items())
425425

426426
def __setattr__(self, name, value): # pylint: disable=invalid-name
427427
self[name] = value
@@ -469,7 +469,7 @@ def _maybe_make(meas):
469469
"""Turn strings into Measurement objects if necessary."""
470470
if isinstance(meas, Measurement):
471471
return meas
472-
elif isinstance(meas, basestring):
472+
elif isinstance(meas, str):
473473
return Measurement(meas, **kwargs)
474474
raise InvalidMeasurementType('Expected Measurement or string', meas)
475475

openhtf/core/phase_executor.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -91,12 +91,13 @@ class PhaseExecutionOutcome(collections.namedtuple(
9191
other value will raise an InvalidPhaseResultError.
9292
"""
9393

94-
def __init__(self, phase_result):
94+
def __new__(cls, phase_result):
9595
if (phase_result is not None and
9696
not isinstance(phase_result, (openhtf.PhaseResult, ExceptionInfo)) and
9797
not isinstance(phase_result, threads.ThreadTerminationError)):
9898
raise InvalidPhaseResultError('Invalid phase result', phase_result)
99-
super(PhaseExecutionOutcome, self).__init__(phase_result)
99+
self = super(PhaseExecutionOutcome, cls).__new__(cls, phase_result)
100+
return self
100101

101102
@property
102103
def is_fail_and_continue(self):
@@ -208,7 +209,7 @@ def execute_phase(self, phase):
208209
hit its limit for repetitions.
209210
"""
210211
repeat_count = 1
211-
repeat_limit = phase.options.repeat_limit or sys.maxint
212+
repeat_limit = phase.options.repeat_limit or sys.maxsize
212213
while not self._stopping.is_set():
213214
is_last_repeat = repeat_count >= repeat_limit
214215
phase_execution_outcome = self._execute_phase_once(phase, is_last_repeat)
@@ -260,7 +261,7 @@ def stop(self, timeout_s=None):
260261

261262
if phase_thread.is_alive():
262263
phase_thread.kill()
263-
264+
264265
_LOG.debug('Waiting for cancelled phase to exit: %s', phase_thread)
265266
timeout = timeouts.PolledTimeout.from_seconds(timeout_s)
266267
while phase_thread.is_alive() and not timeout.has_expired():

openhtf/core/station_api.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@
6969
import socket
7070
import threading
7171
import time
72-
import xmlrpclib
72+
import xmlrpc.client
7373

7474
import mutablerecords
7575

@@ -83,10 +83,11 @@
8383
from openhtf.util import threads
8484
from openhtf.util import timeouts
8585
from openhtf.util import xmlrpcutil
86+
from past.builtins import long
8687

8788
# Fix for xmlrpclib to use <i8> for longs and ints instead of <int>, because our
8889
# timestamps are in millis, which are too big for 4-byte ints.
89-
xmlrpclib.Marshaller.dispatch[long] = xmlrpclib.Marshaller.dispatch[int] = (
90+
xmlrpc.client.Marshaller.dispatch[long] = xmlrpc.client.Marshaller.dispatch[int] = (
9091
lambda _, v, w: w('<value><i8>%d</i8></value>' % v))
9192

9293
_LOG = logging.getLogger(__name__)
@@ -408,7 +409,7 @@ def wait_for_update(self, timeout_s=1):
408409
try:
409410
remote_state_dict = self.proxy_factory(timeout_s + 1).wait_for_update(
410411
self.test_uid, summary_dict, timeout_s)
411-
except xmlrpclib.Fault as fault:
412+
except xmlrpc.client.Fault as fault:
412413
# TODO(madsci): This is a super kludge, eventually implement the
413414
# ReraisingMixin for ServerProxy, but that's hard, so do this for now.
414415
if 'openhtf.io.station_api.UpdateTimeout' in fault.faultString:
@@ -770,7 +771,7 @@ def wait_for_plug_update(self, test_uid, plug_name, current_state, timeout_s):
770771
def _summary_for_state_dict(state_dict):
771772
"""Return a dict for state with counts swapped in for phase/log records."""
772773
state_dict_summary = {
773-
k: v for k, v in state_dict.iteritems() if k != 'plugs'}
774+
k: v for k, v in state_dict.items() if k != 'plugs'}
774775
state_dict_summary['test_record'] = data.convert_to_base_types(
775776
state_dict_summary['test_record'])
776777
state_dict_summary['test_record']['phases'] = len(

openhtf/core/test_executor.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,8 @@ def finalize(self):
109109
def wait(self):
110110
"""Waits until death."""
111111
try:
112-
self.join(sys.float_info.max) # Timeout needed for SIGINT handling.
112+
# Timeout needed for SIGINT handling. Timeout is not actually used.
113+
self.join(60)
113114
except KeyboardInterrupt:
114115
self.test_state.logger.info('KeyboardInterrupt caught, aborting test.')
115116
raise

openhtf/core/test_record.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ def _get_source_safely(obj):
125125
return ''
126126

127127

128-
class CodeInfo(mutablerecords.Record(
128+
class CodeInfo(mutablerecords.HashableRecord(
129129
'CodeInfo', ['name', 'docstring', 'sourcecode'])):
130130
"""Information regarding the running tester code."""
131131

0 commit comments

Comments
 (0)