Merge lp:~florent.x/pyflakes/1097061-unittest2 into lp:~divmod-dev/pyflakes/trunk
- 1097061-unittest2
- Merge into trunk
Status: | Merged | ||||
---|---|---|---|---|---|
Merged at revision: | 49 | ||||
Proposed branch: | lp:~florent.x/pyflakes/1097061-unittest2 | ||||
Merge into: | lp:~divmod-dev/pyflakes/trunk | ||||
Diff against target: |
616 lines (+119/-165) 6 files modified
.travis.yml (+3/-2) pyflakes/test/harness.py (+3/-3) pyflakes/test/test_imports.py (+6/-7) pyflakes/test/test_other.py (+5/-20) pyflakes/test/test_script.py (+100/-131) pyflakes/test/test_undefined_names.py (+2/-2) |
||||
To merge this branch: | bzr merge lp:~florent.x/pyflakes/1097061-unittest2 | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Florent (community) | self-review | Approve | |
Divmod-dev | Pending | ||
Jean-Paul Calderone | Pending | ||
Review via email:
|
Commit message
Description of the change
Enhancement for continuous integration and coverage of Python version 2.5 (and 3.x in the near future)
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Jean-Paul Calderone (exarkun) wrote : | # |
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Florent (florent.x) wrote : | # |
To be fair, the testsuite was dependent on Twisted previously, even if it was not declared in setup.py.
Here, the optional import gives a benefit for commands like:
$ python setup.py test
This allows to run the tests simply, and it takes care of installing the 'test_requires' dependency without other action (without any assumption on the test framework: nose, py.test, twisted.trial, unittest, etc...).
This particular use case is not supported by the distutils library.
In addition, the optional dependency is not on setuptools exclusively. It works perfectly with distribute as well.
I just tried without distribute/
I could change setup.py in order to add 'test_requires'
For the record, there are some users which use the advanced features of distribute/
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Florent (florent.x) wrote : | # |
> Here, the optional import gives a benefit for commands like:
>
> $ python setup.py test
>
Another verb which is widely used but not supported by distutils:
$ python setup.py develop
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Jean-Paul Calderone (exarkun) wrote : | # |
> To be fair, the testsuite was dependent on Twisted previously, even if it was not declared in setup.py.
Yes. That was part of my point. It depended on Twisted and did not declare it in setup.py. You can switch to unittest2 and still not declare it it setup.py. The change to introduce a setuptools dependency can be considered *completely* separately from the change to use unittest2.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Florent (florent.x) wrote : | # |
Sorry, but I fail to understand the reasoning to reject the setuptools optional dependency.
It makes testing easier and more standard for the developer.
Since the 'tests_require' contains information about the tests requirements, anyone can run the tests without digging in the source code to know which libraries are required.
And when the distribute/
If the request is to split the merge proposal into 2 patches ... why not.
For me, they are complementary changes to improve testing, so it makes sense to propose them together.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Jean-Paul Calderone (exarkun) wrote : | # |
> It makes testing easier and more standard for the developer.
It doesn't do either of these.
> Since the 'tests_require' contains information about the tests requirements, anyone can run the tests without digging in the source code to know which libraries are required.
This can be addressed just as easily with an INSTALL or README file.
> And when the distribute/
So there are now twice as many possible install outcomes, which makes debugging installation problems (at least) twice as hard.
> If the request is to split the merge proposal into 2 patches ... why not.
Yes, that's what I was requesting (so that I can only reject the setuptools change while not rejecting the unittest2 change).
> For me, they are complementary changes to improve testing, so it makes sense to propose them together.
Many, many, many, many changes are complementary. That doesn't mean they should be made simultaneously. Changesets should focus on one change, not many complementary changes. Basically, the smallest useful change is what you should try to make. That way issues with *unrelated* changes don't hold up useful improvements, reviews are easier, the history is easier to navigate, tickets can have simple descriptions, and problems that require changes to be reverted don't also revert working functionality.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Florent (florent.x) wrote : | # |
The branch was replaced according to your request.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Glyph Lefkowitz (glyph) wrote : | # |
On Jan 8, 2013, at 8:20 AM, Jean-Paul Calderone <email address hidden> wrote:
>> If the request is to split the merge proposal into 2 patches ... why not.
>
> Yes, that's what I was requesting (so that I can only reject the setuptools change while not rejecting the unittest2 change).
>
>> For me, they are complementary changes to improve testing, so it makes sense to propose them together.
>
> Many, many, many, many changes are complementary. That doesn't mean they should be made simultaneously. Changesets should focus on one change, not many complementary changes. Basically, the smallest useful change is what you should try to make. That way issues with *unrelated* changes don't hold up useful improvements, reviews are easier, the history is easier to navigate, tickets can have simple descriptions, and problems that require changes to be reverted don't also revert working functionality.
My feelings about setuptools are somewhat more ambivalent, but this point is more important. The fact that this discussion about setuptools is happening is now holding up an unrelated change. If the changes were split up, the less controversial parts (which are by far the larger part of these changes) could be discussed on their own merits and landed much more quickly.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Florent (florent.x) wrote : | # |
This branch contains only the replacement of Twisted with unittest2 and is ready for review.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Florent (florent.x) wrote : | # |
LGTM ;)
Preview Diff
1 | === modified file '.travis.yml' |
2 | --- .travis.yml 2013-01-07 17:24:54 +0000 |
3 | +++ .travis.yml 2013-01-08 22:58:19 +0000 |
4 | @@ -1,13 +1,14 @@ |
5 | language: python |
6 | python: |
7 | + - 2.5 |
8 | - 2.6 |
9 | - 2.7 |
10 | - pypy |
11 | install: |
12 | - python setup.py install |
13 | - - pip install Twisted |
14 | + - pip install unittest2 |
15 | script: |
16 | - - trial pyflakes/test |
17 | + - python -m unittest2 discover |
18 | matrix: |
19 | allow_failures: |
20 | - python: pypy |
21 | |
22 | === modified file 'pyflakes/test/harness.py' |
23 | --- pyflakes/test/harness.py 2010-04-13 14:53:04 +0000 |
24 | +++ pyflakes/test/harness.py 2013-01-08 22:58:19 +0000 |
25 | @@ -2,12 +2,12 @@ |
26 | import textwrap |
27 | import _ast |
28 | |
29 | -from twisted.trial import unittest |
30 | +import unittest2 |
31 | |
32 | from pyflakes import checker |
33 | |
34 | |
35 | -class Test(unittest.TestCase): |
36 | +class Test(unittest2.TestCase): |
37 | |
38 | def flakes(self, input, *expectedOutputs, **kw): |
39 | ast = compile(textwrap.dedent(input), "<test>", "exec", |
40 | @@ -17,7 +17,7 @@ |
41 | expectedOutputs = list(expectedOutputs) |
42 | outputs.sort() |
43 | expectedOutputs.sort() |
44 | - self.assert_(outputs == expectedOutputs, '''\ |
45 | + self.assertEqual(outputs, expectedOutputs, '''\ |
46 | for input: |
47 | %s |
48 | expected outputs: |
49 | |
50 | === modified file 'pyflakes/test/test_imports.py' |
51 | --- pyflakes/test/test_imports.py 2011-10-31 15:19:35 +0000 |
52 | +++ pyflakes/test/test_imports.py 2013-01-08 22:58:19 +0000 |
53 | @@ -1,5 +1,6 @@ |
54 | |
55 | from sys import version_info |
56 | +from unittest2 import skip, skipIf |
57 | |
58 | from pyflakes import messages as m |
59 | from pyflakes.test import harness |
60 | @@ -446,6 +447,7 @@ |
61 | self.flakes('import fu; [fu, bar] = fu') |
62 | self.flakes('import fu; fu += fu') |
63 | |
64 | + @skip("todo") |
65 | def test_tryingMultipleImports(self): |
66 | self.flakes(''' |
67 | try: |
68 | @@ -453,7 +455,6 @@ |
69 | except ImportError: |
70 | import bar as fu |
71 | ''') |
72 | - test_tryingMultipleImports.todo = '' |
73 | |
74 | def test_nonGlobalDoesNotRedefine(self): |
75 | self.flakes(''' |
76 | @@ -479,6 +480,7 @@ |
77 | fu |
78 | ''', m.RedefinedWhileUnused) |
79 | |
80 | + @skip("todo") |
81 | def test_importingForImportError(self): |
82 | self.flakes(''' |
83 | try: |
84 | @@ -486,8 +488,8 @@ |
85 | except ImportError: |
86 | pass |
87 | ''') |
88 | - test_importingForImportError.todo = '' |
89 | |
90 | + @skip("todo: requires evaluating attribute access") |
91 | def test_importedInClass(self): |
92 | '''Imports in class scope can be used through self''' |
93 | self.flakes(''' |
94 | @@ -496,7 +498,6 @@ |
95 | def __init__(self): |
96 | self.i |
97 | ''') |
98 | - test_importedInClass.todo = 'requires evaluating attribute access' |
99 | |
100 | def test_futureImport(self): |
101 | '''__future__ is special''' |
102 | @@ -639,10 +640,8 @@ |
103 | """ |
104 | Tests for checking of syntax which is valid in PYthon 2.6 and newer. |
105 | """ |
106 | - if version_info < (2, 6): |
107 | - skip = "Python 2.6 required for class decorator tests." |
108 | - |
109 | - |
110 | + |
111 | + @skipIf(version_info < (2, 6), "Python >= 2.6 only") |
112 | def test_usedAsClassDecorator(self): |
113 | """ |
114 | Using an imported name as a class decorator results in no warnings, |
115 | |
116 | === modified file 'pyflakes/test/test_other.py' |
117 | --- pyflakes/test/test_other.py 2011-11-17 16:21:58 +0000 |
118 | +++ pyflakes/test/test_other.py 2013-01-08 22:58:19 +0000 |
119 | @@ -6,6 +6,7 @@ |
120 | """ |
121 | |
122 | from sys import version_info |
123 | +from unittest2 import skip, skipIf |
124 | |
125 | from pyflakes import messages as m |
126 | from pyflakes.test import harness |
127 | @@ -16,6 +17,7 @@ |
128 | def test_duplicateArgs(self): |
129 | self.flakes('def fu(bar, bar): pass', m.DuplicateArgument) |
130 | |
131 | + @skip("todo: this requires finding all assignments in the function body first") |
132 | def test_localReferencedBeforeAssignment(self): |
133 | self.flakes(''' |
134 | a = 1 |
135 | @@ -23,7 +25,6 @@ |
136 | a; a=1 |
137 | f() |
138 | ''', m.UndefinedName) |
139 | - test_localReferencedBeforeAssignment.todo = 'this requires finding all assignments in the function body first' |
140 | |
141 | def test_redefinedFunction(self): |
142 | """ |
143 | @@ -148,6 +149,7 @@ |
144 | ''', m.RedefinedWhileUnused) |
145 | |
146 | |
147 | + @skip("todo: Too hard to make this warn but other cases stay silent") |
148 | def test_doubleAssignment(self): |
149 | """ |
150 | If a variable is re-assigned to without being used, no warning is |
151 | @@ -158,8 +160,6 @@ |
152 | x = 10 |
153 | x = 20 |
154 | ''', m.RedefinedWhileUnused) |
155 | - test_doubleAssignment.todo = ( |
156 | - "Too hard to make this warn but other cases stay silent") |
157 | |
158 | |
159 | def test_doubleAssignmentConditionally(self): |
160 | @@ -406,14 +406,6 @@ |
161 | ''') |
162 | |
163 | |
164 | - |
165 | -class Python25Test(harness.Test): |
166 | - """ |
167 | - Tests for checking of syntax only available in Python 2.5 and newer. |
168 | - """ |
169 | - if version_info < (2, 5): |
170 | - skip = "Python 2.5 required for if-else and with tests" |
171 | - |
172 | def test_ifexp(self): |
173 | """ |
174 | Test C{foo if bar else baz} statements. |
175 | @@ -627,15 +619,7 @@ |
176 | pass |
177 | ''', m.UndefinedName) |
178 | |
179 | - |
180 | - |
181 | -class Python27Test(harness.Test): |
182 | - """ |
183 | - Tests for checking of syntax only available in Python 2.7 and newer. |
184 | - """ |
185 | - if version_info < (2, 7): |
186 | - skip = "Python 2.7 required for dict/set comprehension tests" |
187 | - |
188 | + @skipIf(version_info < (2, 7), "Python >= 2.7 only") |
189 | def test_dictComprehension(self): |
190 | """ |
191 | Dict comprehensions are properly handled. |
192 | @@ -644,6 +628,7 @@ |
193 | a = {1: x for x in range(10)} |
194 | ''') |
195 | |
196 | + @skipIf(version_info < (2, 7), "Python >= 2.7 only") |
197 | def test_setComprehensionAndLiteral(self): |
198 | """ |
199 | Set comprehensions are properly handled. |
200 | |
201 | === modified file 'pyflakes/test/test_script.py' |
202 | --- pyflakes/test/test_script.py 2012-10-23 11:52:32 +0000 |
203 | +++ pyflakes/test/test_script.py 2013-01-08 22:58:19 +0000 |
204 | @@ -4,15 +4,12 @@ |
205 | |
206 | import os |
207 | import sys |
208 | +import shutil |
209 | +import subprocess |
210 | +import tempfile |
211 | from StringIO import StringIO |
212 | |
213 | -from twisted.internet import protocol |
214 | -from twisted.internet.utils import ( |
215 | - _callProtocolWithDeferred, |
216 | - getProcessOutputAndValue, |
217 | - ) |
218 | -from twisted.python.filepath import FilePath |
219 | -from twisted.trial.unittest import TestCase |
220 | +from unittest2 import TestCase |
221 | |
222 | from pyflakes.messages import UnusedImport |
223 | from pyflakes.reporter import Reporter |
224 | @@ -20,7 +17,7 @@ |
225 | checkPath, |
226 | checkRecursive, |
227 | iterSourceCode, |
228 | - ) |
229 | +) |
230 | |
231 | |
232 | def withStderrTo(stderr, f, *args, **kwargs): |
233 | @@ -34,7 +31,6 @@ |
234 | sys.stderr = outer |
235 | |
236 | |
237 | - |
238 | class LoggingReporter(object): |
239 | """ |
240 | Implementation of Reporter that just appends any errors to a list. |
241 | @@ -67,13 +63,25 @@ |
242 | Tests for L{iterSourceCode}. |
243 | """ |
244 | |
245 | + def setUp(self): |
246 | + self.tempdir = tempfile.mkdtemp() |
247 | + |
248 | + def tearDown(self): |
249 | + shutil.rmtree(self.tempdir) |
250 | + |
251 | + def makeEmptyFile(self, *parts): |
252 | + assert parts |
253 | + fpath = os.path.join(self.tempdir, *parts) |
254 | + fd = open(fpath, 'a') |
255 | + fd.close() |
256 | + return fpath |
257 | + |
258 | + |
259 | def test_emptyDirectory(self): |
260 | """ |
261 | There are no Python files in an empty directory. |
262 | """ |
263 | - tempdir = FilePath(self.mktemp()) |
264 | - tempdir.createDirectory() |
265 | - self.assertEqual(list(iterSourceCode([tempdir.path])), []) |
266 | + self.assertEqual(list(iterSourceCode([self.tempdir])), []) |
267 | |
268 | |
269 | def test_singleFile(self): |
270 | @@ -81,22 +89,16 @@ |
271 | If the directory contains one Python file, C{iterSourceCode} will find |
272 | it. |
273 | """ |
274 | - tempdir = FilePath(self.mktemp()) |
275 | - tempdir.createDirectory() |
276 | - tempdir.child('foo.py').touch() |
277 | - self.assertEqual( |
278 | - list(iterSourceCode([tempdir.path])), |
279 | - [tempdir.child('foo.py').path]) |
280 | + childpath = self.makeEmptyFile('foo.py') |
281 | + self.assertEqual(list(iterSourceCode([self.tempdir])), [childpath]) |
282 | |
283 | |
284 | def test_onlyPythonSource(self): |
285 | """ |
286 | Files that are not Python source files are not included. |
287 | """ |
288 | - tempdir = FilePath(self.mktemp()) |
289 | - tempdir.createDirectory() |
290 | - tempdir.child('foo.pyc').touch() |
291 | - self.assertEqual(list(iterSourceCode([tempdir.path])), []) |
292 | + self.makeEmptyFile('foo.pyc') |
293 | + self.assertEqual(list(iterSourceCode([self.tempdir])), []) |
294 | |
295 | |
296 | def test_recurses(self): |
297 | @@ -104,18 +106,14 @@ |
298 | If the Python files are hidden deep down in child directories, we will |
299 | find them. |
300 | """ |
301 | - tempdir = FilePath(self.mktemp()) |
302 | - tempdir.createDirectory() |
303 | - tempdir.child('foo').createDirectory() |
304 | - tempdir.child('foo').child('a.py').touch() |
305 | - tempdir.child('bar').createDirectory() |
306 | - tempdir.child('bar').child('b.py').touch() |
307 | - tempdir.child('c.py').touch() |
308 | + os.mkdir(os.path.join(self.tempdir, 'foo')) |
309 | + apath = self.makeEmptyFile('foo', 'a.py') |
310 | + os.mkdir(os.path.join(self.tempdir, 'bar')) |
311 | + bpath = self.makeEmptyFile('bar', 'b.py') |
312 | + cpath = self.makeEmptyFile('c.py') |
313 | self.assertEqual( |
314 | - sorted(iterSourceCode([tempdir.path])), |
315 | - sorted([tempdir.child('foo').child('a.py').path, |
316 | - tempdir.child('bar').child('b.py').path, |
317 | - tempdir.child('c.py').path])) |
318 | + sorted(iterSourceCode([self.tempdir])), |
319 | + sorted([apath, bpath, cpath])) |
320 | |
321 | |
322 | def test_multipleDirectories(self): |
323 | @@ -123,18 +121,15 @@ |
324 | L{iterSourceCode} can be given multiple directories. It will recurse |
325 | into each of them. |
326 | """ |
327 | - tempdir = FilePath(self.mktemp()) |
328 | - tempdir.createDirectory() |
329 | - foo = tempdir.child('foo') |
330 | - foo.createDirectory() |
331 | - foo.child('a.py').touch() |
332 | - bar = tempdir.child('bar') |
333 | - bar.createDirectory() |
334 | - bar.child('b.py').touch() |
335 | + foopath = os.path.join(self.tempdir, 'foo') |
336 | + barpath = os.path.join(self.tempdir, 'bar') |
337 | + os.mkdir(foopath) |
338 | + apath = self.makeEmptyFile('foo', 'a.py') |
339 | + os.mkdir(barpath) |
340 | + bpath = self.makeEmptyFile('bar', 'b.py') |
341 | self.assertEqual( |
342 | - sorted(iterSourceCode([foo.path, bar.path])), |
343 | - sorted([foo.child('a.py').path, |
344 | - bar.child('b.py').path])) |
345 | + sorted(iterSourceCode([foopath, barpath])), |
346 | + sorted([apath, bpath])) |
347 | |
348 | |
349 | def test_explicitFiles(self): |
350 | @@ -142,10 +137,9 @@ |
351 | If one of the paths given to L{iterSourceCode} is not a directory but |
352 | a file, it will include that in its output. |
353 | """ |
354 | - tempfile = FilePath(self.mktemp()) |
355 | - tempfile.touch() |
356 | - self.assertEqual(list(iterSourceCode([tempfile.path])), |
357 | - [tempfile.path]) |
358 | + epath = self.makeEmptyFile('e.py') |
359 | + self.assertEqual(list(iterSourceCode([epath])), |
360 | + [epath]) |
361 | |
362 | |
363 | |
364 | @@ -239,9 +233,11 @@ |
365 | """ |
366 | Make a temporary file containing C{content} and return a path to it. |
367 | """ |
368 | - path = FilePath(self.mktemp()) |
369 | - path.setContent(content) |
370 | - return path.path |
371 | + _, fpath = tempfile.mkstemp() |
372 | + fd = open(fpath, 'wb') |
373 | + fd.write(content) |
374 | + fd.close() |
375 | + return fpath |
376 | |
377 | |
378 | def assertHasErrors(self, path, errorList): |
379 | @@ -314,8 +310,12 @@ |
380 | # isn't, something this test was unprepared for has happened. |
381 | def evaluate(source): |
382 | exec source |
383 | - exc = self.assertRaises(SyntaxError, evaluate, source) |
384 | - self.assertTrue(exc.text.count('\n') > 1) |
385 | + try: |
386 | + evaluate(source) |
387 | + except SyntaxError, e: |
388 | + self.assertTrue(e.text.count('\n') > 1) |
389 | + else: |
390 | + self.fail() |
391 | |
392 | sourcePath = self.makeTempFile(source) |
393 | self.assertHasErrors( |
394 | @@ -384,14 +384,13 @@ |
395 | If the source file is not readable, this is reported on standard |
396 | error. |
397 | """ |
398 | - sourcePath = FilePath(self.mktemp()) |
399 | - sourcePath.setContent('') |
400 | - sourcePath.chmod(0) |
401 | - count, errors = self.getErrors(sourcePath.path) |
402 | + sourcePath = self.makeTempFile('') |
403 | + os.chmod(sourcePath, 0) |
404 | + count, errors = self.getErrors(sourcePath) |
405 | self.assertEquals(count, 1) |
406 | self.assertEquals( |
407 | errors, |
408 | - [('unexpectedError', sourcePath.path, "Permission denied")]) |
409 | + [('unexpectedError', sourcePath, "Permission denied")]) |
410 | |
411 | |
412 | def test_pyflakesWarning(self): |
413 | @@ -425,59 +424,25 @@ |
414 | L{checkRecursive} descends into each directory, finding Python files |
415 | and reporting problems. |
416 | """ |
417 | - tempdir = FilePath(self.mktemp()) |
418 | - tempdir.createDirectory() |
419 | - tempdir.child('foo').createDirectory() |
420 | - file1 = tempdir.child('foo').child('bar.py') |
421 | - file1.setContent("import baz\n") |
422 | - file2 = tempdir.child('baz.py') |
423 | - file2.setContent("import contraband") |
424 | + tempdir = tempfile.mkdtemp() |
425 | + os.mkdir(os.path.join(tempdir, 'foo')) |
426 | + file1 = os.path.join(tempdir, 'foo', 'bar.py') |
427 | + fd = open(file1, 'wb') |
428 | + fd.write("import baz\n") |
429 | + fd.close() |
430 | + file2 = os.path.join(tempdir, 'baz.py') |
431 | + fd = open(file2, 'wb') |
432 | + fd.write("import contraband") |
433 | + fd.close() |
434 | log = [] |
435 | reporter = LoggingReporter(log) |
436 | - warnings = checkRecursive([tempdir.path], reporter) |
437 | + warnings = checkRecursive([tempdir], reporter) |
438 | self.assertEqual(warnings, 2) |
439 | self.assertEqual( |
440 | sorted(log), |
441 | - sorted([('flake', str(UnusedImport(file1.path, 1, 'baz'))), |
442 | + sorted([('flake', str(UnusedImport(file1, 1, 'baz'))), |
443 | ('flake', |
444 | - str(UnusedImport(file2.path, 1, 'contraband')))])) |
445 | - |
446 | - |
447 | - |
448 | -class _EverythingGetterWithStdin(protocol.ProcessProtocol): |
449 | - """ |
450 | - C{ProcessProtocol} that writes to stdin and gathers exit code, stdout and |
451 | - stderr. |
452 | - """ |
453 | - |
454 | - # Although Twisted provides a helper that spawns a subprocess, gathers its |
455 | - # exit code, standard output and standard error |
456 | - # (i.e. getProcessOutputAndValue), it does *not* provide one that data to |
457 | - # be written to stdin first. |
458 | - |
459 | - def __init__(self, deferred, stdin): |
460 | - self.deferred = deferred |
461 | - self.outBuf = StringIO() |
462 | - self.errBuf = StringIO() |
463 | - self.outReceived = self.outBuf.write |
464 | - self.errReceived = self.errBuf.write |
465 | - self.stdin = stdin |
466 | - |
467 | - |
468 | - def connectionMade(self): |
469 | - self.transport.write(self.stdin) |
470 | - self.transport.closeStdin() |
471 | - |
472 | - |
473 | - def processEnded(self, reason): |
474 | - out = self.outBuf.getvalue() |
475 | - err = self.errBuf.getvalue() |
476 | - e = reason.value |
477 | - code = e.exitCode |
478 | - if e.signal: |
479 | - code = -e.signal |
480 | - self.deferred.callback((out, err, code)) |
481 | - |
482 | + str(UnusedImport(file2, 1, 'contraband')))])) |
483 | |
484 | |
485 | class IntegrationTests(TestCase): |
486 | @@ -485,13 +450,20 @@ |
487 | Tests of the pyflakes script that actually spawn the script. |
488 | """ |
489 | |
490 | + def setUp(self): |
491 | + self.tempdir = tempfile.mkdtemp() |
492 | + self.tempfilepath = os.path.join(self.tempdir, 'temp') |
493 | + |
494 | + def tearDown(self): |
495 | + shutil.rmtree(self.tempdir) |
496 | + |
497 | def getPyflakesBinary(self): |
498 | """ |
499 | Return the path to the pyflakes binary. |
500 | """ |
501 | import pyflakes |
502 | - package_dir = FilePath(pyflakes.__file__).parent() |
503 | - return package_dir.sibling('bin').child('pyflakes').path |
504 | + package_dir = os.path.dirname(pyflakes.__file__) |
505 | + return os.path.join(package_dir, '..', 'bin', 'pyflakes') |
506 | |
507 | |
508 | def runPyflakes(self, paths, stdin=None): |
509 | @@ -505,15 +477,18 @@ |
510 | """ |
511 | env = dict(os.environ) |
512 | env['PYTHONPATH'] = os.pathsep.join(sys.path) |
513 | - command = [self.getPyflakesBinary()] |
514 | + command = [sys.executable, self.getPyflakesBinary()] |
515 | command.extend(paths) |
516 | if stdin: |
517 | - d = _callProtocolWithDeferred( |
518 | - lambda d: _EverythingGetterWithStdin(d, stdin), |
519 | - sys.executable, command, env=env, path=None) |
520 | + p = subprocess.Popen(command, env=env, stdin=subprocess.PIPE, |
521 | + stdout=subprocess.PIPE, stderr=subprocess.PIPE) |
522 | + (stdout, stderr) = p.communicate(stdin) |
523 | else: |
524 | - d = getProcessOutputAndValue(sys.executable, command, env=env) |
525 | - return d |
526 | + p = subprocess.Popen(command, env=env, |
527 | + stdout=subprocess.PIPE, stderr=subprocess.PIPE) |
528 | + (stdout, stderr) = p.communicate() |
529 | + rv = p.wait() |
530 | + return (stdout, stderr, rv) |
531 | |
532 | |
533 | def test_goodFile(self): |
534 | @@ -521,10 +496,10 @@ |
535 | When a Python source file is all good, the return code is zero and no |
536 | messages are printed to either stdout or stderr. |
537 | """ |
538 | - tempfile = FilePath(self.mktemp()) |
539 | - tempfile.touch() |
540 | - d = self.runPyflakes([tempfile.path]) |
541 | - return d.addCallback(self.assertEqual, ('', '', 0)) |
542 | + fd = open(self.tempfilepath, 'a') |
543 | + fd.close() |
544 | + d = self.runPyflakes([self.tempfilepath]) |
545 | + self.assertEqual(d, ('', '', 0)) |
546 | |
547 | |
548 | def test_fileWithFlakes(self): |
549 | @@ -532,12 +507,11 @@ |
550 | When a Python source file has warnings, the return code is non-zero |
551 | and the warnings are printed to stdout. |
552 | """ |
553 | - tempfile = FilePath(self.mktemp()) |
554 | - tempfile.setContent("import contraband\n") |
555 | - d = self.runPyflakes([tempfile.path]) |
556 | - return d.addCallback( |
557 | - self.assertEqual, |
558 | - ("%s\n" % UnusedImport(tempfile.path, 1, 'contraband'), '', 1)) |
559 | + fd = open(self.tempfilepath, 'wb') |
560 | + fd.write("import contraband\n") |
561 | + fd.close() |
562 | + d = self.runPyflakes([self.tempfilepath]) |
563 | + self.assertEqual(d, ("%s\n" % UnusedImport(self.tempfilepath, 1, 'contraband'), '', 1)) |
564 | |
565 | |
566 | def test_errors(self): |
567 | @@ -546,11 +520,8 @@ |
568 | exist, say), then the return code is non-zero and the errors are |
569 | printed to stderr. |
570 | """ |
571 | - tempfile = FilePath(self.mktemp()) |
572 | - d = self.runPyflakes([tempfile.path]) |
573 | - return d.addCallback( |
574 | - self.assertEqual, |
575 | - ('', '%s: No such file or directory\n' % (tempfile.path,), 1)) |
576 | + d = self.runPyflakes([self.tempfilepath]) |
577 | + self.assertEqual(d, ('', '%s: No such file or directory\n' % (self.tempfilepath,), 1)) |
578 | |
579 | |
580 | def test_readFromStdin(self): |
581 | @@ -558,6 +529,4 @@ |
582 | If no arguments are passed to C{pyflakes} then it reads from stdin. |
583 | """ |
584 | d = self.runPyflakes([], stdin='import contraband') |
585 | - return d.addCallback( |
586 | - self.assertEqual, |
587 | - ("%s\n" % UnusedImport('<stdin>', 1, 'contraband'), '', 1)) |
588 | + self.assertEqual(d, ("%s\n" % UnusedImport('<stdin>', 1, 'contraband'), '', 1)) |
589 | |
590 | === modified file 'pyflakes/test/test_undefined_names.py' |
591 | --- pyflakes/test/test_undefined_names.py 2011-12-04 20:27:45 +0000 |
592 | +++ pyflakes/test/test_undefined_names.py 2013-01-08 22:58:19 +0000 |
593 | @@ -1,7 +1,7 @@ |
594 | |
595 | from _ast import PyCF_ONLY_AST |
596 | |
597 | -from twisted.trial.unittest import TestCase |
598 | +from unittest2 import skip, TestCase |
599 | |
600 | from pyflakes import messages as m, checker |
601 | from pyflakes.test import harness |
602 | @@ -87,13 +87,13 @@ |
603 | bar; baz |
604 | ''') |
605 | |
606 | + @skip("todo") |
607 | def test_definedByGlobal(self): |
608 | '''"global" can make an otherwise undefined name in another function defined''' |
609 | self.flakes(''' |
610 | def a(): global fu; fu = 1 |
611 | def b(): fu |
612 | ''') |
613 | - test_definedByGlobal.todo = '' |
614 | |
615 | def test_globalInGlobalScope(self): |
616 | """ |
I'd prefer not to see Pyflakes gain a setuptools dependency (even an optional one). setuptools causes far more problems than it solves. Regardless, as a part of this branch, the addition seems out of place - Pyflakes' setup.py did not declare a dependency on Twisted previously (in other words, it's an unrelated "enhancement").