Merge lp:~gz/brz/py3_rio into lp:brz

Proposed by Martin Packman
Status: Merged
Approved by: Martin Packman
Approved revision: no longer in the source branch.
Merge reported by: The Breezy Bot
Merged at revision: not available
Proposed branch: lp:~gz/brz/py3_rio
Merge into: lp:brz
Prerequisite: lp:~gz/brz/py3_bootstrap2
Diff against target: 435 lines (+86/-82)
3 files modified
breezy/rio.py (+31/-29)
breezy/tests/test__rio.py (+24/-15)
breezy/tests/test_rio.py (+31/-38)
To merge this branch: bzr merge lp:~gz/brz/py3_rio
Reviewer Review Type Date Requested Status
Jelmer Vernooij Approve
Review via email: mp+325459@code.launchpad.net

Commit message

Make rio work with Python 3

Description of the change

Change code and tests for the rio module to work with Python 3.

For now, the _rio_pyx helper is still 2.7 only.

To post a comment you must log in.
Revision history for this message
Jelmer Vernooij (jelmer) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'breezy/rio.py'
2--- breezy/rio.py 2017-06-10 01:57:00 +0000
3+++ breezy/rio.py 2017-06-11 17:04:24 +0000
4@@ -174,17 +174,19 @@
5 # max() complains if sequence is empty
6 return []
7 result = []
8- for tag, value in self.items:
9- if value == '':
10- result.append(tag.encode('ascii') + b': \n')
11- elif '\n' in value:
12+ for text_tag, text_value in self.items:
13+ tag = text_tag.encode('ascii')
14+ value = text_value.encode('utf-8')
15+ if value == b'':
16+ result.append(tag + b': \n')
17+ elif b'\n' in value:
18 # don't want splitlines behaviour on empty lines
19- val_lines = value.split('\n')
20- result.append(tag + b': ' + val_lines[0].encode('utf-8') + b'\n')
21+ val_lines = value.split(b'\n')
22+ result.append(tag + b': ' + val_lines[0] + b'\n')
23 for line in val_lines[1:]:
24- result.append(b'\t' + line.encode('utf-8') + b'\n')
25+ result.append(b'\t' + line + b'\n')
26 else:
27- result.append(tag.encode('ascii') + b': ' + value.encode('utf-8') + b'\n')
28+ result.append(tag + b': ' + value + b'\n')
29 return result
30
31 def to_string(self):
32@@ -302,61 +304,61 @@
33 max_rio_width = max_width - 4
34 lines = []
35 for pline in stanza.to_lines():
36- for line in pline.split('\n')[:-1]:
37- line = re.sub('\\\\', '\\\\\\\\', line)
38+ for line in pline.split(b'\n')[:-1]:
39+ line = re.sub(b'\\\\', b'\\\\\\\\', line)
40 while len(line) > 0:
41 partline = line[:max_rio_width]
42 line = line[max_rio_width:]
43- if len(line) > 0 and line[0] != [' ']:
44+ if len(line) > 0 and line[:1] != [b' ']:
45 break_index = -1
46- break_index = partline.rfind(' ', -20)
47+ break_index = partline.rfind(b' ', -20)
48 if break_index < 3:
49- break_index = partline.rfind('-', -20)
50+ break_index = partline.rfind(b'-', -20)
51 break_index += 1
52 if break_index < 3:
53- break_index = partline.rfind('/', -20)
54+ break_index = partline.rfind(b'/', -20)
55 if break_index >= 3:
56 line = partline[break_index:] + line
57 partline = partline[:break_index]
58 if len(line) > 0:
59- line = ' ' + line
60- partline = re.sub('\r', '\\\\r', partline)
61+ line = b' ' + line
62+ partline = re.sub(b'\r', b'\\\\r', partline)
63 blank_line = False
64 if len(line) > 0:
65- partline += '\\'
66- elif re.search(' $', partline):
67- partline += '\\'
68+ partline += b'\\'
69+ elif re.search(b' $', partline):
70+ partline += b'\\'
71 blank_line = True
72- lines.append('# ' + partline + '\n')
73+ lines.append(b'# ' + partline + b'\n')
74 if blank_line:
75- lines.append('# \n')
76+ lines.append(b'# \n')
77 return lines
78
79
80 def _patch_stanza_iter(line_iter):
81- map = {'\\\\': '\\',
82- '\\r' : '\r',
83- '\\\n': ''}
84+ map = {b'\\\\': b'\\',
85+ b'\\r' : b'\r',
86+ b'\\\n': b''}
87 def mapget(match):
88 return map[match.group(0)]
89
90 last_line = None
91 for line in line_iter:
92- if line.startswith('# '):
93+ if line.startswith(b'# '):
94 line = line[2:]
95- elif line.startswith('#'):
96+ elif line.startswith(b'#'):
97 line = line[1:]
98 else:
99 raise ValueError("bad line %r" % (line,))
100 if last_line is not None and len(line) > 2:
101 line = line[2:]
102- line = re.sub('\r', '', line)
103- line = re.sub('\\\\(.|\n)', mapget, line)
104+ line = re.sub(b'\r', b'', line)
105+ line = re.sub(b'\\\\(.|\n)', mapget, line)
106 if last_line is None:
107 last_line = line
108 else:
109 last_line += line
110- if last_line[-1] == '\n':
111+ if last_line[-1:] == b'\n':
112 yield last_line
113 last_line = None
114 if last_line is not None:
115
116=== modified file 'breezy/tests/test__rio.py'
117--- breezy/tests/test__rio.py 2017-05-23 14:08:03 +0000
118+++ breezy/tests/test__rio.py 2017-06-11 17:04:24 +0000
119@@ -16,10 +16,15 @@
120
121 """Tests for _rio_*."""
122
123-from breezy import (
124+from __future__ import absolute_import
125+
126+from .. import (
127 rio,
128 tests,
129 )
130+from ..sixish import (
131+ text_type,
132+ )
133
134
135 def load_tests(loader, standard_tests, pattern):
136@@ -43,7 +48,7 @@
137
138 def test_no_colon(self):
139 self.assertFalse(self.module._valid_tag("foo:bla"))
140-
141+
142 def test_type_error(self):
143 self.assertRaises(TypeError, self.module._valid_tag, 423)
144
145@@ -51,7 +56,11 @@
146 self.assertFalse(self.module._valid_tag(""))
147
148 def test_unicode(self):
149- self.assertRaises(TypeError, self.module._valid_tag, u"foo")
150+ if text_type is str:
151+ # When str is a unicode type, it is valid for a tag
152+ self.assertTrue(self.module._valid_tag(u"foo"))
153+ else:
154+ self.assertRaises(TypeError, self.module._valid_tag, u"foo")
155
156 def test_non_ascii_char(self):
157 self.assertFalse(self.module._valid_tag("\xb5"))
158@@ -67,7 +76,7 @@
159 if s is not None:
160 for tag, value in s.iter_pairs():
161 self.assertIsInstance(tag, str)
162- self.assertIsInstance(value, unicode)
163+ self.assertIsInstance(value, text_type)
164
165 def assertReadStanzaRaises(self, exception, line_iter):
166 self.assertRaises(exception, self.module._read_stanza_utf8, line_iter)
167@@ -79,34 +88,34 @@
168 self.assertReadStanza(None, [])
169
170 def test_none(self):
171- self.assertReadStanza(None, [""])
172+ self.assertReadStanza(None, [b""])
173
174 def test_simple(self):
175- self.assertReadStanza(rio.Stanza(foo="bar"), ["foo: bar\n", ""])
176+ self.assertReadStanza(rio.Stanza(foo="bar"), [b"foo: bar\n", b""])
177
178 def test_multi_line(self):
179- self.assertReadStanza(rio.Stanza(foo="bar\nbla"),
180- ["foo: bar\n", "\tbla\n"])
181+ self.assertReadStanza(
182+ rio.Stanza(foo="bar\nbla"), [b"foo: bar\n", b"\tbla\n"])
183
184 def test_repeated(self):
185 s = rio.Stanza()
186 s.add("foo", "bar")
187 s.add("foo", "foo")
188- self.assertReadStanza(s, ["foo: bar\n", "foo: foo\n"])
189+ self.assertReadStanza(s, [b"foo: bar\n", b"foo: foo\n"])
190
191 def test_invalid_early_colon(self):
192- self.assertReadStanzaRaises(ValueError, ["f:oo: bar\n"])
193+ self.assertReadStanzaRaises(ValueError, [b"f:oo: bar\n"])
194
195 def test_invalid_tag(self):
196- self.assertReadStanzaRaises(ValueError, ["f%oo: bar\n"])
197+ self.assertReadStanzaRaises(ValueError, [b"f%oo: bar\n"])
198
199 def test_continuation_too_early(self):
200- self.assertReadStanzaRaises(ValueError, ["\tbar\n"])
201+ self.assertReadStanzaRaises(ValueError, [b"\tbar\n"])
202
203 def test_large(self):
204- value = "bla" * 9000
205+ value = b"bla" * 9000
206 self.assertReadStanza(rio.Stanza(foo=value),
207- ["foo: %s\n" % value])
208+ [b"foo: %s\n" % value])
209
210 def test_non_ascii_char(self):
211 self.assertReadStanza(rio.Stanza(foo=u"n\xe5me"),
212@@ -123,7 +132,7 @@
213 if s is not None:
214 for tag, value in s.iter_pairs():
215 self.assertIsInstance(tag, str)
216- self.assertIsInstance(value, unicode)
217+ self.assertIsInstance(value, text_type)
218
219 def assertReadStanzaRaises(self, exception, line_iter):
220 self.assertRaises(exception, self.module._read_stanza_unicode,
221
222=== modified file 'breezy/tests/test_rio.py'
223--- breezy/tests/test_rio.py 2017-05-24 19:44:00 +0000
224+++ breezy/tests/test_rio.py 2017-06-11 17:04:24 +0000
225@@ -51,26 +51,18 @@
226 self.assertEqual(s.get('number'), '42')
227 self.assertEqual(s.get('name'), 'fred')
228
229- def test_value_checks(self):
230- """rio checks types on construction"""
231- # these aren't enforced at construction time
232- ## self.assertRaises(ValueError,
233- ## Stanza, complex=42 + 3j)
234- ## self.assertRaises(ValueError,
235- ## Stanza, several=range(10))
236-
237 def test_empty_value(self):
238 """Serialize stanza with empty field"""
239 s = Stanza(empty='')
240- self.assertEqualDiff(s.to_string(),
241- "empty: \n")
242+ self.assertEquals(s.to_string(),
243+ b"empty: \n")
244
245 def test_to_lines(self):
246 """Write simple rio stanza to string"""
247 s = Stanza(number='42', name='fred')
248 self.assertEqual(list(s.to_lines()),
249- ['name: fred\n',
250- 'number: 42\n'])
251+ [b'name: fred\n',
252+ b'number: 42\n'])
253
254 def test_as_dict(self):
255 """Convert rio Stanza to dictionary"""
256@@ -84,18 +76,18 @@
257 s = Stanza(a_thing='something with "quotes like \\"this\\""', number='42', name='fred')
258 s.write(tmpf)
259 tmpf.seek(0)
260- self.assertEqualDiff(tmpf.read(), r'''
261-a_thing: something with "quotes like \"this\""
262+ self.assertEqual(tmpf.read(), b'''\
263+a_thing: something with "quotes like \\"this\\""
264 name: fred
265 number: 42
266-'''[1:])
267+''')
268
269 def test_multiline_string(self):
270 tmpf = TemporaryFile()
271 s = Stanza(motto="war is peace\nfreedom is slavery\nignorance is strength")
272 s.write(tmpf)
273 tmpf.seek(0)
274- self.assertEqualDiff(tmpf.read(), '''\
275+ self.assertEqual(tmpf.read(), b'''\
276 motto: war is peace
277 \tfreedom is slavery
278 \tignorance is strength
279@@ -106,7 +98,7 @@
280
281 def test_read_stanza(self):
282 """Load stanza from string"""
283- lines = """\
284+ lines = b"""\
285 revision: mbp@sourcefrog.net-123-abc
286 timestamp: 1130653962
287 timezone: 36000
288@@ -114,7 +106,7 @@
289 """.splitlines(True)
290 s = read_stanza(lines)
291 self.assertTrue('revision' in s)
292- self.assertEqualDiff(s.get('revision'), 'mbp@sourcefrog.net-123-abc')
293+ self.assertEqual(s.get('revision'), 'mbp@sourcefrog.net-123-abc')
294 self.assertEqual(list(s.iter_pairs()),
295 [('revision', 'mbp@sourcefrog.net-123-abc'),
296 ('timestamp', '1130653962'),
297@@ -136,13 +128,13 @@
298 def test_backslash(self):
299 s = Stanza(q='\\')
300 t = s.to_string()
301- self.assertEqualDiff(t, 'q: \\\n')
302+ self.assertEqual(t, b'q: \\\n')
303 s2 = read_stanza(s.to_lines())
304 self.assertEqual(s, s2)
305
306 def test_blank_line(self):
307 s = Stanza(none='', one='\n', two='\n\n')
308- self.assertEqualDiff(s.to_string(), """\
309+ self.assertEqual(s.to_string(), b"""\
310 none:\x20
311 one:\x20
312 \t
313@@ -155,7 +147,7 @@
314
315 def test_whitespace_value(self):
316 s = Stanza(space=' ', tabs='\t\t\t', combo='\n\t\t\n')
317- self.assertEqualDiff(s.to_string(), """\
318+ self.assertEqual(s.to_string(), b"""\
319 combo:\x20
320 \t\t\t
321 \t
322@@ -192,16 +184,16 @@
323
324 def test_read_nul_byte(self):
325 """File consisting of a nul byte causes an error."""
326- self.assertRaises(ValueError, read_stanza, ['\0'])
327+ self.assertRaises(ValueError, read_stanza, [b'\0'])
328
329 def test_read_nul_bytes(self):
330 """File consisting of many nul bytes causes an error."""
331- self.assertRaises(ValueError, read_stanza, ['\0' * 100])
332+ self.assertRaises(ValueError, read_stanza, [b'\0' * 100])
333
334 def test_read_iter(self):
335 """Read several stanzas from file"""
336 tmpf = TemporaryFile()
337- tmpf.write("""\
338+ tmpf.write(b"""\
339 version_header: 1
340
341 name: foo
342@@ -222,7 +214,7 @@
343 def test_read_several(self):
344 """Read several stanzas from file"""
345 tmpf = TemporaryFile()
346- tmpf.write("""\
347+ tmpf.write(b"""\
348 version_header: 1
349
350 name: foo
351@@ -242,8 +234,9 @@
352 s = read_stanza(tmpf)
353 self.assertEqual(s, Stanza(name="foo", val='123'))
354 s = read_stanza(tmpf)
355- self.assertEqualDiff(s.get('name'), 'quoted')
356- self.assertEqualDiff(s.get('address'), ' "Willowglen"\n 42 Wallaby Way\n Sydney')
357+ self.assertEqual(s.get('name'), 'quoted')
358+ self.assertEqual(
359+ s.get('address'), ' "Willowglen"\n 42 Wallaby Way\n Sydney')
360 s = read_stanza(tmpf)
361 self.assertEqual(s, Stanza(name="bar", val='129319'))
362 s = read_stanza(tmpf)
363@@ -266,7 +259,7 @@
364
365 def test_tricky_quoted(self):
366 tmpf = TemporaryFile()
367- tmpf.write('''\
368+ tmpf.write(b'''\
369 s: "one"
370
371 s:\x20
372@@ -316,7 +309,7 @@
373 stanza = read_stanza(tmpf)
374 self.rio_file_stanzas([stanza])
375 self.assertEqual(len(stanza), 1)
376- self.assertEqualDiff(stanza.get('s'), expected)
377+ self.assertEqual(stanza.get('s'), expected)
378
379 def test_write_empty_stanza(self):
380 """Write empty stanza"""
381@@ -339,7 +332,7 @@
382 self.assertEqual(s.get('foo'), uni_data)
383 raw_lines = s.to_lines()
384 self.assertEqual(raw_lines,
385- ['foo: ' + uni_data.encode('utf-8') + '\n'])
386+ [b'foo: ' + uni_data.encode('utf-8') + b'\n'])
387 new_s = read_stanza(raw_lines)
388 self.assertEqual(new_s.get('foo'), uni_data)
389
390@@ -356,8 +349,8 @@
391 s = Stanza(foo=uni_data)
392 parent_stanza = Stanza(child=s.to_unicode())
393 raw_lines = parent_stanza.to_lines()
394- self.assertEqual(['child: foo: ' + uni_data.encode('utf-8') + '\n',
395- '\t\n',
396+ self.assertEqual([b'child: foo: ' + uni_data.encode('utf-8') + b'\n',
397+ b'\t\n',
398 ], raw_lines)
399 new_parent = read_stanza(raw_lines)
400 child_text = new_parent.get('child')
401@@ -368,9 +361,9 @@
402 def mail_munge(self, lines, dos_nl=True):
403 new_lines = []
404 for line in lines:
405- line = re.sub(' *\n', '\n', line)
406+ line = re.sub(b' *\n', b'\n', line)
407 if dos_nl:
408- line = re.sub('([^\r])\n', '\\1\r\n', line)
409+ line = re.sub(b'([^\r])\n', b'\\1\r\n', line)
410 new_lines.append(line)
411 return new_lines
412
413@@ -378,7 +371,7 @@
414 stanza = Stanza(data='#\n\r\\r ', space=' ' * 255, hash='#' * 255)
415 lines = rio.to_patch_lines(stanza)
416 for line in lines:
417- self.assertContainsRe(line, '^# ')
418+ self.assertContainsRe(line, b'^# ')
419 self.assertTrue(72 >= len(line))
420 for line in rio.to_patch_lines(stanza, max_width=12):
421 self.assertTrue(12 >= len(line))
422@@ -393,10 +386,10 @@
423 def test_patch_rio_linebreaks(self):
424 stanza = Stanza(breaktest='linebreak -/'*30)
425 self.assertContainsRe(rio.to_patch_lines(stanza, 71)[0],
426- 'linebreak\\\\\n')
427+ b'linebreak\\\\\n')
428 stanza = Stanza(breaktest='linebreak-/'*30)
429 self.assertContainsRe(rio.to_patch_lines(stanza, 70)[0],
430- 'linebreak-\\\\\n')
431+ b'linebreak-\\\\\n')
432 stanza = Stanza(breaktest='linebreak/'*30)
433 self.assertContainsRe(rio.to_patch_lines(stanza, 70)[0],
434- 'linebreak\\\\\n')
435+ b'linebreak\\\\\n')

Subscribers

People subscribed via source and target branches