Merge lp:~gz/bzr/python_2.7_selftest_582113 into lp:bzr

Proposed by Martin Packman
Status: Merged
Approved by: John A Meinel
Approved revision: no longer in the source branch.
Merged at revision: 5372
Proposed branch: lp:~gz/bzr/python_2.7_selftest_582113
Merge into: lp:bzr
Diff against target: 228 lines (+40/-29)
3 files modified
bzrlib/tests/TestUtil.py (+7/-1)
bzrlib/tests/__init__.py (+26/-24)
bzrlib/tests/test_selftest.py (+7/-4)
To merge this branch: bzr merge lp:~gz/bzr/python_2.7_selftest_582113
Reviewer Review Type Date Requested Status
John A Meinel Approve
Review via email: mp+30332@code.launchpad.net

Commit message

Some python2.7 unittest fixes (_WriteLnDecorator, etc)

Description of the change

Get bzr selftest working on Python 2.7 by resolving some incompatibilities with the many changes to the unittest module (now a package).

Removes use of unittest._WriteLnDecorator which isn't really very helpful anyway. Also switches to using testtools.TextTestResult which shouldn't break the output any more than the original testtools switch already has - there aren't new test failures*.

Getting this branch up on babune is probably a precursor to deciding if it's practical to support Python 2.7 with the bzr 2.2 release, I'm tending towards 'no' currently as this was a pain.

I've not tested this all that thoroughly, so if something breaks for you, please yell.

* Okay, there are new test failures on Python 2.7 but for other reasons.

To post a comment you must log in.
Revision history for this message
Barry Warsaw (barry) wrote :

JAM and I were looking at this today, and started down the _WriteLnDecorator change you made (but you did it more thoroughly). I'm not really qualified to review the patch for bzr, but +1 on supporting Python 2.7 if you can do it without break promises for earlier Pythons.

Revision history for this message
John A Meinel (jameinel) wrote :
Download full text (4.0 KiB)

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Martin [gz] wrote:
> Martin [gz] has proposed merging lp:~gz/bzr/python_2.7_selftest_582113 into lp:bzr.
>
> Requested reviews:
> bzr-core (bzr-core)
> Related bugs:
> #582113 Tests don't run under Python 2.7 (unittest._WritelnDecorator has moved)
> https://bugs.launchpad.net/bugs/582113
>
>
> Get bzr selftest working on Python 2.7 by resolving some incompatibilities with the many changes to the unittest module (now a package).
>
> Removes use of unittest._WriteLnDecorator which isn't really very helpful anyway. Also switches to using testtools.TextTestResult which shouldn't break the output any more than the original testtools switch already has - there aren't new test failures*.
>
> Getting this branch up on babune is probably a precursor to deciding if it's practical to support Python 2.7 with the bzr 2.2 release, I'm tending towards 'no' currently as this was a pain.
>
> I've not tested this all that thoroughly, so if something breaks for you, please yell.
>
> * Okay, there are new test failures on Python 2.7 but for other reasons.
>

- - basic_tests = super(TestLoader, self).loadTestsFromModule(module)
+ basic_tests = super(TestLoader, self).loadTestsFromModule(module,
+ # GZ 2010-07-19: Python 2.7 unittest also uses load_tests but
with a
+ # different incompatible signature so have to avoid
+ **(sys.version_info > (2, 7) and {"use_load_tests": False}
or {}))

^- spell this out, this is pretty ugly as is.

kwargs = {}
if sys.version >= (2, 7):
  # Python 2.7 unittest supports load_tests, but using an incompatible
  # signature, so disable it
  kwargs['use_load_tests'] = False
basic_tests = super(TestLoader, self).loadTestsFromModule(module,
    **kwargs)

- - unittest._TextTestResult.__init__(self, stream, descriptions,
verbosity)
+ testtools.TextTestResult.__init__(self, stream)

^- Losing the parameters seems iffy, but if you are sure it is correct.
...

- - self.printErrors()
- - self.stream.writeln(self.separator2)
- - self.stream.writeln("%s %d test%s in %.3fs" % (actionTaken,
+ # GZ 2010-07-19: Seems testtools has no printErrors method, and
though
+ # the parent class method is similar have to
duplicate
+ self._show_list('ERROR', self.errors)
+ self._show_list('FAIL', self.failures)
+ self.stream.write(self.sep2)
+ self.stream.write("%s %d test%s in %.3fs\n\n" % (actionTaken,
                             run, run != 1 and "s" or "", timeTaken))
- - self.stream.writeln()

^- I'm happy to get rid of 'writeln()' though I wonder how much of
*unittest* expects it to be there. unittest._TextTestResult certainly
seems to.
I'm just concerned that we are technically breaking compatibility in
ways we won't find until a test fails, or we get an exception, or some
other non-standard case.

self.separator2 => self.sep2 also seems to be an odd change. (Either way
this seems to be something that should be private)

At the least, shouldn't we pull this code block into our own helper as a
replacement for 'printErrors()' ?

...

@@ -878...

Read more...

review: Needs Fixing
Revision history for this message
Vincent Ladeuil (vila) wrote :

As a data point:

  http://babune.ladeuil.net:24842/job/selftest-maverick/6/console

This was with python2.7 from ppa:doko/toolchain

Revision history for this message
John A Meinel (jameinel) wrote :

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Vincent Ladeuil wrote:
> As a data point:
>
> http://babune.ladeuil.net:24842/job/selftest-maverick/6/console
>
> This was with python2.7 from ppa:doko/toolchain
>

Is this his code or trunk?

Looks like we got a unicode decoding exception which broke the subunit
toolchain...

John
=:->

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.9 (Cygwin)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/

iEYEARECAAYFAkxFz6cACgkQJdeBCYSNAAMltwCg2d/IjNPPP2meEh6Myy9heT7p
4WQAoNS5a5qQPPCC6onyjKKCAcAUYafL
=gyUh
-----END PGP SIGNATURE-----

Revision history for this message
Martin Packman (gz) wrote :

> JAM and I were looking at this today, and started down the _WriteLnDecorator
> change you made (but you did it more thoroughly). I'm not really qualified to
> review the patch for bzr, but +1 on supporting Python 2.7 if you can do it
> without break promises for earlier Pythons.

I've filed a few more bugs with a python2.7 tag, most of these are also due to bazaar extending unittest, but might be relevent for a few other projects.

> ^- spell this out, this is pretty ugly as is.

It's a fair cop guv. In my defence, this code is due to be ripped out when a resolution for bug 607412 is decided on.

> ^- Losing the parameters seems iffy, but if you are sure it is correct.

This and the following couple of comments are this way because the testtools testresult classes are a parallel implementation, not unittest subclasses.

> ^- I'm happy to get rid of 'writeln()' though I wonder how much of
> *unittest* expects it to be there. unittest._TextTestResult certainly
> seems to.

Only unittest classes we're no longer using expect a writeln method.

> self.separator2 => self.sep2 also seems to be an odd change. (Either way
> this seems to be something that should be private)

I presume the name change was done in testtools because the values are not strictly equivalent, unittest doesn't include the newline.

> At the least, shouldn't we pull this code block into our own helper as a
> replacement for 'printErrors()' ?

Yes, that's one option. I'd like testtools to improve here instead, its TextTestResult.stopTestRun method is nearly identical to the bzr one, just doesn't have any way of adding in the bzr specific bits by subclassing, so this is all duplication. Filed bug 607435 against testtools on that.

> ^- Do you have more info on this? loosing messages seems like a rather
> bad thing.

That's bug 607400 - filed after I wrote the code, which is why the lack of bug numbers, can add those in.

> As a data point:
>
> http://babune.ladeuil.net:24842/job/selftest-maverick/6/console
>
> This was with python2.7 from ppa:doko/toolchain

Ack, wasn't expecting that fast a response, thanks! The problem there is it needs the fix for testtools bug 604187 which hasn't been merged yet - or the previous testtools release would work as well I think.

Revision history for this message
Vincent Ladeuil (vila) wrote :

This is clearly going in the right direction but can't be landed as is so marking WIP.

Let's make sure we get the dependencies on testtools/subunit right too or it will be hard to review.

Revision history for this message
Martin Packman (gz) wrote :

Addressed John's point about the ugly workaround spelling, and replaced a few more unittest.TestResult method calls I somehow missed, which maybe caused the babune problems.

Elsewhere have fixed another testtools bug I'm to blame for, and Jonathan Lange has released 0.9.5 but there may still be another issue there or in subunit that needs shaking out. Another babune run with that updated as well would be great.

Revision history for this message
John A Meinel (jameinel) wrote :

Seems fine to me. I'm not sure what we need from testtools for python 2.7 support. Any chance you could clarify?

review: Approve
Revision history for this message
Martin Packman (gz) wrote :

The bit of my last comment talking about testtools was mostly addressed to Vincent - trying to run this branch with 2.7 on babune had some python-or-bzr-or-testtools-or-subunit fallout with streams leaking into other streams.

To answer you exact question, we don't need anything more from testtools for 2.7. Using their TextTestRunner avoids some unittest backwards compatibility problems, but there are still some other minor issues which I've filed bugs for.

Revision history for this message
John A Meinel (jameinel) wrote :

sent to pqm by email

Revision history for this message
Martin Packman (gz) wrote :

This was a prerequisite for the other two _python_2.7_ branches so was silently merged when the first of those was anyway.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'bzrlib/tests/TestUtil.py'
2--- bzrlib/tests/TestUtil.py 2010-02-17 17:11:16 +0000
3+++ bzrlib/tests/TestUtil.py 2010-08-05 18:13:49 +0000
4@@ -135,7 +135,13 @@
5 >>> result.addTests([test, test])
6 >>> return result
7 """
8- basic_tests = super(TestLoader, self).loadTestsFromModule(module)
9+ if sys.version_info < (2, 7):
10+ basic_tests = super(TestLoader, self).loadTestsFromModule(module)
11+ else:
12+ # GZ 2010-07-19: Python 2.7 unittest also uses load_tests but with
13+ # a different and incompatible signature
14+ basic_tests = super(TestLoader, self).loadTestsFromModule(module,
15+ use_load_tests=False)
16 load_tests = getattr(module, "load_tests", None)
17 if load_tests is not None:
18 return load_tests(basic_tests, module, self)
19
20=== modified file 'bzrlib/tests/__init__.py'
21--- bzrlib/tests/__init__.py 2010-08-05 05:41:34 +0000
22+++ bzrlib/tests/__init__.py 2010-08-05 18:13:49 +0000
23@@ -141,7 +141,7 @@
24 SUBUNIT_SEEK_CUR = 1
25
26
27-class ExtendedTestResult(unittest._TextTestResult):
28+class ExtendedTestResult(testtools.TextTestResult):
29 """Accepts, reports and accumulates the results of running tests.
30
31 Compared to the unittest version this class adds support for
32@@ -168,7 +168,7 @@
33 :param bench_history: Optionally, a writable file object to accumulate
34 benchmark results.
35 """
36- unittest._TextTestResult.__init__(self, stream, descriptions, verbosity)
37+ testtools.TextTestResult.__init__(self, stream)
38 if bench_history is not None:
39 from bzrlib.version import _get_bzr_source_tree
40 src_tree = _get_bzr_source_tree()
41@@ -201,11 +201,13 @@
42 actionTaken = "Ran"
43 stopTime = time.time()
44 timeTaken = stopTime - self.startTime
45- self.printErrors()
46- self.stream.writeln(self.separator2)
47- self.stream.writeln("%s %d test%s in %.3fs" % (actionTaken,
48+ # GZ 2010-07-19: Seems testtools has no printErrors method, and though
49+ # the parent class method is similar have to duplicate
50+ self._show_list('ERROR', self.errors)
51+ self._show_list('FAIL', self.failures)
52+ self.stream.write(self.sep2)
53+ self.stream.write("%s %d test%s in %.3fs\n\n" % (actionTaken,
54 run, run != 1 and "s" or "", timeTaken))
55- self.stream.writeln()
56 if not self.wasSuccessful():
57 self.stream.write("FAILED (")
58 failed, errored = map(len, (self.failures, self.errors))
59@@ -218,20 +220,20 @@
60 if failed or errored: self.stream.write(", ")
61 self.stream.write("known_failure_count=%d" %
62 self.known_failure_count)
63- self.stream.writeln(")")
64+ self.stream.write(")\n")
65 else:
66 if self.known_failure_count:
67- self.stream.writeln("OK (known_failures=%d)" %
68+ self.stream.write("OK (known_failures=%d)\n" %
69 self.known_failure_count)
70 else:
71- self.stream.writeln("OK")
72+ self.stream.write("OK\n")
73 if self.skip_count > 0:
74 skipped = self.skip_count
75- self.stream.writeln('%d test%s skipped' %
76+ self.stream.write('%d test%s skipped\n' %
77 (skipped, skipped != 1 and "s" or ""))
78 if self.unsupported:
79 for feature, count in sorted(self.unsupported.items()):
80- self.stream.writeln("Missing feature '%s' skipped %d tests." %
81+ self.stream.write("Missing feature '%s' skipped %d tests.\n" %
82 (feature, count))
83 if self._strict:
84 ok = self.wasStrictlySuccessful()
85@@ -279,7 +281,7 @@
86 return what
87
88 def startTest(self, test):
89- unittest.TestResult.startTest(self, test)
90+ super(ExtendedTestResult, self).startTest(test)
91 if self.count == 0:
92 self.startTests()
93 self.report_test_start(test)
94@@ -323,7 +325,7 @@
95 fails with an unexpected error.
96 """
97 self._post_mortem()
98- unittest.TestResult.addError(self, test, err)
99+ super(ExtendedTestResult, self).addError(test, err)
100 self.error_count += 1
101 self.report_error(test, err)
102 if self.stop_early:
103@@ -337,7 +339,7 @@
104 fails because e.g. an assert() method failed.
105 """
106 self._post_mortem()
107- unittest.TestResult.addFailure(self, test, err)
108+ super(ExtendedTestResult, self).addFailure(test, err)
109 self.failure_count += 1
110 self.report_failure(test, err)
111 if self.stop_early:
112@@ -357,7 +359,7 @@
113 test.id()))
114 self.report_success(test)
115 self._cleanupLogFile(test)
116- unittest.TestResult.addSuccess(self, test)
117+ super(ExtendedTestResult, self).addSuccess(test)
118 test._log_contents = ''
119
120 def addExpectedFailure(self, test, err):
121@@ -551,40 +553,40 @@
122 return '%s%s' % (indent, err[1])
123
124 def report_error(self, test, err):
125- self.stream.writeln('ERROR %s\n%s'
126+ self.stream.write('ERROR %s\n%s\n'
127 % (self._testTimeString(test),
128 self._error_summary(err)))
129
130 def report_failure(self, test, err):
131- self.stream.writeln(' FAIL %s\n%s'
132+ self.stream.write(' FAIL %s\n%s\n'
133 % (self._testTimeString(test),
134 self._error_summary(err)))
135
136 def report_known_failure(self, test, err):
137- self.stream.writeln('XFAIL %s\n%s'
138+ self.stream.write('XFAIL %s\n%s\n'
139 % (self._testTimeString(test),
140 self._error_summary(err)))
141
142 def report_success(self, test):
143- self.stream.writeln(' OK %s' % self._testTimeString(test))
144+ self.stream.write(' OK %s\n' % self._testTimeString(test))
145 for bench_called, stats in getattr(test, '_benchcalls', []):
146- self.stream.writeln('LSProf output for %s(%s, %s)' % bench_called)
147+ self.stream.write('LSProf output for %s(%s, %s)\n' % bench_called)
148 stats.pprint(file=self.stream)
149 # flush the stream so that we get smooth output. This verbose mode is
150 # used to show the output in PQM.
151 self.stream.flush()
152
153 def report_skip(self, test, reason):
154- self.stream.writeln(' SKIP %s\n%s'
155+ self.stream.write(' SKIP %s\n%s\n'
156 % (self._testTimeString(test), reason))
157
158 def report_not_applicable(self, test, reason):
159- self.stream.writeln(' N/A %s\n %s'
160+ self.stream.write(' N/A %s\n %s\n'
161 % (self._testTimeString(test), reason))
162
163 def report_unsupported(self, test, feature):
164 """test cannot be run because feature is missing."""
165- self.stream.writeln("NODEP %s\n The feature '%s' is not available."
166+ self.stream.write("NODEP %s\n The feature '%s' is not available.\n"
167 %(self._testTimeString(test), feature))
168
169
170@@ -619,7 +621,7 @@
171 encode = codec.encode
172 stream = osutils.UnicodeOrBytesToBytesWriter(encode, stream)
173 stream.encoding = new_encoding
174- self.stream = unittest._WritelnDecorator(stream)
175+ self.stream = stream
176 self.descriptions = descriptions
177 self.verbosity = verbosity
178 self._bench_history = bench_history
179
180=== modified file 'bzrlib/tests/test_selftest.py'
181--- bzrlib/tests/test_selftest.py 2010-07-23 18:45:31 +0000
182+++ bzrlib/tests/test_selftest.py 2010-08-05 18:13:49 +0000
183@@ -801,7 +801,7 @@
184 self.requireFeature(test_lsprof.LSProfFeature)
185 result_stream = StringIO()
186 result = bzrlib.tests.VerboseTestResult(
187- unittest._WritelnDecorator(result_stream),
188+ result_stream,
189 descriptions=0,
190 verbosity=2,
191 )
192@@ -862,7 +862,7 @@
193 # verbose test output formatting
194 result_stream = StringIO()
195 result = bzrlib.tests.VerboseTestResult(
196- unittest._WritelnDecorator(result_stream),
197+ result_stream,
198 descriptions=0,
199 verbosity=2,
200 )
201@@ -878,6 +878,9 @@
202 output = result_stream.getvalue()[prefix:]
203 lines = output.splitlines()
204 self.assertContainsRe(lines[0], r'XFAIL *\d+ms$')
205+ if sys.version_info > (2, 7):
206+ self.expectFailure("_ExpectedFailure on 2.7 loses the message",
207+ self.assertNotEqual, lines[1], ' ')
208 self.assertEqual(lines[1], ' foo')
209 self.assertEqual(2, len(lines))
210
211@@ -917,7 +920,7 @@
212 # verbose test output formatting
213 result_stream = StringIO()
214 result = bzrlib.tests.VerboseTestResult(
215- unittest._WritelnDecorator(result_stream),
216+ result_stream,
217 descriptions=0,
218 verbosity=2,
219 )
220@@ -1419,7 +1422,7 @@
221 sample_test = TestTestCase("method_that_times_a_bit_twice")
222 output_stream = StringIO()
223 result = bzrlib.tests.VerboseTestResult(
224- unittest._WritelnDecorator(output_stream),
225+ output_stream,
226 descriptions=0,
227 verbosity=2)
228 sample_test.run(result)