Merge lp:~abentley/juju-ci-tools/more-py3-2 into lp:juju-ci-tools

Proposed by Aaron Bentley
Status: Merged
Merged at revision: 1932
Proposed branch: lp:~abentley/juju-ci-tools/more-py3-2
Merge into: lp:juju-ci-tools
Prerequisite: lp:~abentley/juju-ci-tools/more-py3
Diff against target: 396 lines (+59/-42)
7 files modified
jujupy/client.py (+5/-3)
jujupy/fake.py (+1/-1)
jujupy/tests/test_client.py (+29/-22)
jujupy/tests/test_version_client.py (+8/-8)
jujupy/utility.py (+2/-2)
jujupy/version_client.py (+3/-1)
tests/__init__.py (+11/-5)
To merge this branch: bzr merge lp:~abentley/juju-ci-tools/more-py3-2
Reviewer Review Type Date Requested Status
Curtis Hovey (community) code Approve
Review via email: mp+319616@code.launchpad.net

Commit message

Improve Python3 compatibility of jujupy.

Description of the change

This branch improves the Python3 compatibility of jujupy, along the same lines as the previous branch.

To post a comment you must log in.
Revision history for this message
Curtis Hovey (sinzui) wrote :

Thank you.

review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'jujupy/client.py'
2--- jujupy/client.py 2017-03-10 19:57:45 +0000
3+++ jujupy/client.py 2017-03-10 19:57:46 +0000
4@@ -1914,7 +1914,8 @@
5
6 def get_env_option(self, option):
7 """Return the value of the environment's configured option."""
8- return self.get_juju_output('model-config', option)
9+ return self.get_juju_output(
10+ 'model-config', option).decode(getpreferredencoding())
11
12 def set_env_option(self, option, value):
13 """Set the value of the option in the environment."""
14@@ -2474,7 +2475,8 @@
15 log.info(e.output)
16 raise
17 log.info(output)
18- backup_file_pattern = re.compile('(juju-backup-[0-9-]+\.(t|tar.)gz)')
19+ backup_file_pattern = re.compile(
20+ '(juju-backup-[0-9-]+\.(t|tar.)gz)'.encode('ascii'))
21 match = backup_file_pattern.search(output)
22 if match is None:
23 raise Exception("The backup file was not found in output: %s" %
24@@ -2482,7 +2484,7 @@
25 backup_file_name = match.group(1)
26 backup_file_path = os.path.abspath(backup_file_name)
27 log.info("State-Server backup at %s", backup_file_path)
28- return backup_file_path
29+ return backup_file_path.decode(getpreferredencoding())
30
31 def restore_backup(self, backup_file):
32 self.juju(
33
34=== modified file 'jujupy/fake.py'
35--- jujupy/fake.py 2017-03-10 19:57:45 +0000
36+++ jujupy/fake.py 2017-03-10 19:57:46 +0000
37@@ -1050,7 +1050,7 @@
38
39
40 def get_user_register_token(username):
41- return b64encode(sha512(username).digest())
42+ return b64encode(sha512(username.encode('utf-8')).digest()).decode('ascii')
43
44
45 class FakeBackend2_1(FakeBackend):
46
47=== modified file 'jujupy/tests/test_client.py'
48--- jujupy/tests/test_client.py 2017-03-10 19:57:45 +0000
49+++ jujupy/tests/test_client.py 2017-03-10 19:57:46 +0000
50@@ -337,9 +337,9 @@
51 self.assertEqual(300, conditions.timeout)
52
53 def test_iter_blocking_state(self):
54- mock_ab = Mock()
55+ mock_ab = Mock(timeout=0)
56 mock_ab.iter_blocking_state.return_value = [('a', 'b')]
57- mock_cd = Mock()
58+ mock_cd = Mock(timeout=0)
59 mock_cd.iter_blocking_state.return_value = [('c', 'd')]
60 conditions = ConditionList([mock_ab, mock_cd])
61 self.assertEqual([('a', 'b'), ('c', 'd')],
62@@ -425,14 +425,15 @@
63 self.assertEqual(client.env.juju_home, 'baz')
64
65 def test_get_version(self):
66- value = ' 5.6 \n'
67+ value = ' 5.6 \n'.encode('ascii')
68 with patch('subprocess.check_output', return_value=value) as vsn:
69 version = ModelClient.get_version()
70 self.assertEqual('5.6', version)
71 vsn.assert_called_with(('juju', '--version'))
72
73 def test_get_version_path(self):
74- with patch('subprocess.check_output', return_value=' 4.3') as vsn:
75+ with patch('subprocess.check_output',
76+ return_value=' 4.3'.encode('ascii')) as vsn:
77 ModelClient.get_version('foo/bar/baz')
78 vsn.assert_called_once_with(('foo/bar/baz', '--version'))
79
80@@ -982,7 +983,7 @@
81 fake_popen = FakePopen('asdf', None, 0)
82 with patch('subprocess.Popen', return_value=fake_popen) as mock:
83 result = client.get_juju_output('bar')
84- self.assertEqual('asdf', result)
85+ self.assertEqual('asdf'.encode('ascii'), result)
86 self.assertEqual((('juju', '--show-log', 'bar', '-m', 'foo:foo'),),
87 mock.call_args[0])
88
89@@ -992,7 +993,7 @@
90 client = ModelClient(env, None, 'juju')
91 with patch('subprocess.Popen', return_value=fake_popen) as mock:
92 result = client.get_juju_output('bar', 'baz', '--qux')
93- self.assertEqual('asdf', result)
94+ self.assertEqual('asdf'.encode('ascii'), result)
95 self.assertEqual((('juju', '--show-log', 'bar', '-m', 'foo:foo', 'baz',
96 '--qux'),), mock.call_args[0])
97
98@@ -1003,7 +1004,7 @@
99 with self.assertRaises(subprocess.CalledProcessError) as exc:
100 with patch('subprocess.Popen', return_value=fake_popen):
101 client.get_juju_output('bar')
102- self.assertEqual(exc.exception.stderr, 'Hello!')
103+ self.assertEqual(exc.exception.stderr, 'Hello!'.encode('ascii'))
104
105 def test_get_juju_output_merge_stderr(self):
106 env = JujuData('foo')
107@@ -1011,7 +1012,7 @@
108 client = ModelClient(env, None, 'juju')
109 with patch('subprocess.Popen', return_value=fake_popen) as mock_popen:
110 result = client.get_juju_output('bar', merge_stderr=True)
111- self.assertEqual(result, 'Err on out')
112+ self.assertEqual(result, 'Err on out'.encode('ascii'))
113 mock_popen.assert_called_once_with(
114 ('juju', '--show-log', 'bar', '-m', 'foo:foo'),
115 stdin=subprocess.PIPE, stderr=subprocess.STDOUT,
116@@ -1062,7 +1063,7 @@
117 - a
118 - b
119 - c
120- """)
121+ """).encode('ascii')
122 env = JujuData('foo')
123 client = ModelClient(env, None, None)
124 with patch.object(client, 'get_juju_output',
125@@ -1080,7 +1081,7 @@
126
127 def get_juju_output(command, *args, **kwargs):
128 if client.attempt == 1:
129- return '"hello"'
130+ return '"hello"'.encode('ascii')
131 client.attempt += 1
132 raise subprocess.CalledProcessError(1, command)
133
134@@ -1370,7 +1371,8 @@
135 with patch.object(client, 'get_juju_output', return_value=status_txt):
136 result = list(client.status_until(-1))
137 self.assertEqual(
138- [r.status for r in result], [Status.from_text(status_txt).status])
139+ [r.status for r in result],
140+ [Status.from_text(status_txt.decode('ascii')).status])
141
142 def test_status_until_timeout(self):
143 client = ModelClient(
144@@ -1710,7 +1712,8 @@
145 'Timed out waiting for agents to start in local'):
146 client.wait_for_started(0)
147 self.assertEqual(writes, ['pending: 0', '\n'])
148- self.assertEqual(self.log_stream.getvalue(), 'ERROR %s\n' % value)
149+ self.assertEqual(
150+ self.log_stream.getvalue(), 'ERROR %s\n' % value.decode('ascii'))
151
152 def test_wait_for_subordinate_units(self):
153 value = dedent("""\
154@@ -1732,7 +1735,7 @@
155 agent-state: started
156 sub3/0:
157 agent-state: started
158- """)
159+ """).encode('ascii')
160 client = ModelClient(JujuData('local'), None, None)
161 now = datetime.now() + timedelta(days=1)
162 with patch('utility.until_timeout.now', return_value=now):
163@@ -1770,7 +1773,7 @@
164 sub3/0:
165 agent-status:
166 current: idle
167- """)
168+ """).encode('ascii')
169 client = ModelClient(JujuData('local'), None, None)
170 now = datetime.now() + timedelta(days=1)
171 with patch('utility.until_timeout.now', return_value=now):
172@@ -1801,7 +1804,7 @@
173 subordinates:
174 sub/1:
175 agent-state: started
176- """)
177+ """).encode('ascii')
178 client = ModelClient(JujuData('local'), None, None)
179 now = datetime.now() + timedelta(days=1)
180 with patch('utility.until_timeout.now', return_value=now):
181@@ -1828,7 +1831,7 @@
182 subordinates:
183 sub1:
184 agent-state: started
185- """)
186+ """).encode('ascii')
187 client = ModelClient(JujuData('local'), None, None)
188 now = datetime.now() + timedelta(days=1)
189 with patch('utility.until_timeout.now', return_value=now):
190@@ -1849,7 +1852,7 @@
191 units:
192 jenkins/0:
193 agent-state: started
194- """)
195+ """).encode('ascii')
196 client = ModelClient(JujuData('local'), None, None)
197 now = datetime.now() + timedelta(days=1)
198 with patch('utility.until_timeout.now', return_value=now):
199@@ -2281,7 +2284,7 @@
200 return client.get_controller_client()
201
202 def test_wait_for_ha(self):
203- value = yaml.safe_dump(self.make_ha_status())
204+ value = yaml.safe_dump(self.make_ha_status()).encode('ascii')
205 client = self.make_controller_client()
206 with patch.object(client, 'get_juju_output',
207 return_value=value) as gjo_mock:
208@@ -2339,7 +2342,7 @@
209 '1': {'agent-state-info': 'error: foo'},
210 },
211 'services': {},
212- })
213+ }).encode('ascii')
214 client = self.make_controller_client()
215 with patch('jujupy.client.until_timeout', autospec=True,
216 return_value=[2, 1]):
217@@ -2371,7 +2374,7 @@
218 }
219 }
220 }
221- })
222+ }).encode('ascii')
223 client = ModelClient(JujuData('local'), None, None)
224 with patch.object(client, 'get_juju_output', return_value=value):
225 client.wait_for_deploy_started()
226@@ -2476,7 +2479,7 @@
227 'machines': {
228 '0': {'agent-state': 'started'},
229 },
230- })
231+ }).encode('ascii')
232 client = ModelClient(JujuData('local'), None, None)
233 with patch.object(client, 'get_juju_output', return_value=value):
234 client.wait_for(WaitMachineNotPresent('1'), quiet=True)
235@@ -4735,7 +4738,7 @@
236
237 def test_from_text(self):
238 text = TestModelClient.make_status_yaml(
239- 'agent-state', 'pending', 'horsefeathers')
240+ 'agent-state', 'pending', 'horsefeathers').decode('ascii')
241 status = Status.from_text(text)
242 self.assertEqual(status.status_text, text)
243 self.assertEqual(status.status, {
244@@ -5939,7 +5942,11 @@
245 status = Status.from_text(self.machine_0_ipv6)
246 fake_client = Mock(spec=['status_until'])
247 fake_client.status_until.return_value = [status]
248+ socket_error = socket.error
249 with patch('jujupy.utility.socket', wraps=socket) as wrapped_socket:
250+ # Must not convert socket.error into a Mock, because Mocks don't
251+ # descend from BaseException
252+ wrapped_socket.error = socket_error
253 del wrapped_socket.inet_pton
254 host = get_machine_dns_name(fake_client, '0')
255 self.assertEqual(host, "2001:db8::3")
256
257=== modified file 'jujupy/tests/test_version_client.py'
258--- jujupy/tests/test_version_client.py 2017-03-10 19:57:45 +0000
259+++ jujupy/tests/test_version_client.py 2017-03-10 19:57:46 +0000
260@@ -657,7 +657,7 @@
261 fake_popen = FakePopen('asdf', None, 0)
262 with patch('subprocess.Popen', return_value=fake_popen) as mock:
263 result = client.get_juju_output('bar')
264- self.assertEqual('asdf', result)
265+ self.assertEqual('asdf', result.encode('ascii'))
266 self.assertEqual((('juju', '--show-log', 'bar', '-e', 'foo'),),
267 mock.call_args[0])
268
269@@ -667,7 +667,7 @@
270 client = EnvJujuClient1X(env, None, 'juju')
271 with patch('subprocess.Popen', return_value=fake_popen) as mock:
272 result = client.get_juju_output('bar', 'baz', '--qux')
273- self.assertEqual('asdf', result)
274+ self.assertEqual('asdf'.encode('ascii'), result)
275 self.assertEqual((('juju', '--show-log', 'bar', '-e', 'foo', 'baz',
276 '--qux'),), mock.call_args[0])
277
278@@ -678,8 +678,8 @@
279 with self.assertRaises(subprocess.CalledProcessError) as exc:
280 with patch('subprocess.Popen', return_value=fake_popen):
281 client.get_juju_output('bar')
282- self.assertEqual(exc.exception.output, 'Hello')
283- self.assertEqual(exc.exception.stderr, 'Error!')
284+ self.assertEqual(exc.exception.output, 'Hello'.encode('ascii'))
285+ self.assertEqual(exc.exception.stderr, 'Error!'.encode('ascii'))
286
287 def test_get_juju_output_full_cmd(self):
288 env = SimpleEnvironment('foo')
289@@ -1046,7 +1046,7 @@
290 subordinates:
291 sub/1:
292 agent-state: started
293- """)
294+ """).encode('ascii')
295 client = EnvJujuClient1X(SimpleEnvironment('local'), None, None)
296 now = datetime.now() + timedelta(days=1)
297 with patch('utility.until_timeout.now', return_value=now):
298@@ -1181,7 +1181,7 @@
299 '2': {'state-server-member-status': 'has-vote'},
300 },
301 'services': {},
302- })
303+ }).encode('ascii')
304 client = EnvJujuClient1X(SimpleEnvironment('local'), None, None)
305 with patch.object(client, 'get_juju_output', return_value=value):
306 client.wait_for_ha()
307@@ -1194,7 +1194,7 @@
308 '2': {'state-server-member-status': 'no-vote'},
309 },
310 'services': {},
311- })
312+ }).encode('ascii')
313 client = EnvJujuClient1X(SimpleEnvironment('local'), None, None)
314 with patch.object(client, 'get_juju_output', return_value=value):
315 writes = []
316@@ -1240,7 +1240,7 @@
317 }
318 }
319 }
320- })
321+ }).encode('ascii')
322 client = EnvJujuClient1X(SimpleEnvironment('local'), None, None)
323 with patch.object(client, 'get_juju_output', return_value=value):
324 client.wait_for_deploy_started()
325
326=== modified file 'jujupy/utility.py'
327--- jujupy/utility.py 2017-02-22 18:05:47 +0000
328+++ jujupy/utility.py 2017-03-10 19:57:46 +0000
329@@ -168,9 +168,9 @@
330
331 @contextmanager
332 def temp_yaml_file(yaml_dict, encoding="utf-8"):
333- temp_file = NamedTemporaryFile(suffix='.yaml', delete=False)
334+ temp_file_cxt = NamedTemporaryFile(suffix='.yaml', delete=False)
335 try:
336- with temp_file:
337+ with temp_file_cxt as temp_file:
338 yaml.safe_dump(yaml_dict, temp_file, encoding=encoding)
339 yield temp_file.name
340 finally:
341
342=== modified file 'jujupy/version_client.py'
343--- jujupy/version_client.py 2017-03-01 19:02:24 +0000
344+++ jujupy/version_client.py 2017-03-10 19:57:46 +0000
345@@ -1,5 +1,6 @@
346 from contextlib import contextmanager
347 import json
348+from locale import getpreferredencoding
349 import logging
350 import os
351 import re
352@@ -370,7 +371,8 @@
353
354 def get_env_option(self, option):
355 """Return the value of the environment's configured option."""
356- return self.get_juju_output('get-env', option)
357+ return self.get_juju_output(
358+ 'get-env', option).decode(getpreferredencoding())
359
360 def set_env_option(self, option, value):
361 """Set the value of the option in the environment."""
362
363=== modified file 'tests/__init__.py'
364--- tests/__init__.py 2017-03-10 19:57:45 +0000
365+++ tests/__init__.py 2017-03-10 19:57:46 +0000
366@@ -173,8 +173,8 @@
367 """Create an artifical version of the Popen class."""
368
369 def __init__(self, out, err, returncode):
370- self._out = out
371- self._err = err
372+ self._out = out if out is None else out.encode('ascii')
373+ self._err = err if err is None else err.encode('ascii')
374 self._code = returncode
375
376 def communicate(self):
377@@ -191,10 +191,16 @@
378 temporary_file = NamedTemporaryFile(delete=False)
379 try:
380 with temporary_file as temp_file:
381+
382+ @contextmanager
383+ def nt():
384+ # This is used to prevent NamedTemporaryFile.close from being
385+ # called.
386+ yield temporary_file
387+
388 with patch('jujupy.utility.NamedTemporaryFile',
389- return_value=temp_file):
390- with patch.object(temp_file, '__exit__'):
391- yield temp_file
392+ return_value=nt()):
393+ yield temp_file
394 finally:
395 # File may have already been deleted, e.g. by temp_yaml_file.
396 with utility.skip_on_missing_file():

Subscribers

People subscribed via source and target branches