Merge lp:~free.ekanayaka/testresources/use-test-case-result into lp:~testresources-developers/testresources/trunk

Proposed by Free Ekanayaka
Status: Needs review
Proposed branch: lp:~free.ekanayaka/testresources/use-test-case-result
Merge into: lp:~testresources-developers/testresources/trunk
Diff against target: 50 lines (+15/-8)
1 file modified
lib/testresources/__init__.py (+15/-8)
To merge this branch: bzr merge lp:~free.ekanayaka/testresources/use-test-case-result
Reviewer Review Type Date Requested Status
testresources developers Pending
Review via email: mp+119153@code.launchpad.net

Description of the change

It seems that the test result is available as private attribute on the test case object, since Python 2.7 (see unittest.case.TestCase.run). This branch makes use of it if available, instead of looking up the stack.

This runs faster and saves a bit of time especially when running a single test.

To post a comment you must log in.
Revision history for this message
Robert Collins (lifeless) wrote :

I'm not sure this is supported - its a _ attribute, and the TestCase
API uses public attributes for things that subclasses are meant to
call. Lets get Michael Foords opinion on it before doing it.

Revision history for this message
Free Ekanayaka (free.ekanayaka) wrote :

Sure, I see the point, but to me accessing that private attribute doesn't seem to leak the underlying implementation any more than traversing the stack looking for a variable with a certain name.

Revision history for this message
Robert Collins (lifeless) wrote :

On Tue, Aug 14, 2012 at 4:45 AM, Free Ekanayaka
<email address hidden> wrote:
> Sure, I see the point, but to me accessing that private attribute doesn't seem to leak the underlying implementation any more than traversing the stack looking for a variable with a certain name.

Its looking for a parameter with a certain name, which is due to
keyword parameters, part of the public contract.

Revision history for this message
Free Ekanayaka (free.ekanayaka) wrote :

Okay fair enough. Shall I get in touch with Michael Foords and ask him how safe/unsafe would be to use that private attribute? or if there is any plan to expose the result object in future Python releases.

Revision history for this message
Robert Collins (lifeless) wrote :

On Tue, Aug 14, 2012 at 9:52 PM, Free Ekanayaka
<email address hidden> wrote:
> Okay fair enough. Shall I get in touch with Michael Foords and ask him how safe/unsafe would be to use that private attribute? or if there is any plan to expose the result object in future Python releases.

His plugins branch might do something in that direction. Please do
have a chat to him :)

Revision history for this message
Free Ekanayaka (free.ekanayaka) wrote :

Hi Robert,

below follows a reply from Michael. Would you be fine with using _resultForDoCleanups for now and possibly file a bug as he suggests?

-----------

Hello Free,

Well, inspecting stack frame locals is a horrible way to find the result. :-)

The correct way to change the result class on the runner is to override the _makeResult method. On the other hand I have no intention of removing _resultForDoCleanups so feel free to use it for the moment and file an issue on bugs.python.org to provide a cleaner way (preferably with some explanation of why you need to access the result and can't override _makeResult or use a custom TestSuite that gives you access to the result object).

It really should be possible to control all of the components of a test stack without resorting to trickery and unittest could certainly use some improvement here.

All the best,

Michael

On 14 Aug 2012, at 12:45, Free Ekanayaka <email address hidden> wrote:

> Hey there,
>
> I'm writing you because of this MP:
>
> https://code.launchpad.net/~free.ekanayaka/testresources/use-test-case-result/+merge/119153
>
> Essentially the testresources package currently finds out the test
> result object with this method:
>
> def _get_result():
> """Find a TestResult in the stack.
>
> unittest hides the result. This forces us to look up the stack.
> The result is passed to a run() or a __call__ method 4 or more frames
> up: that method is what calls setUp and tearDown, and they call their
> parent setUp etc. Its not guaranteed that the parameter to run will
> be calls result as its not required to be a keyword parameter in
> TestCase. However, in practice, this works.
> """
> stack = inspect.stack()
> for frame in stack[2:]:
> if frame[3] in ('run', '__call__'):
> # Not all frames called 'run' will be unittest. It could be a
> # reactor in trial, for instance.
> result = frame[0].f_locals.get('result')
> if (result is not None and
> getattr(result, 'startTest', None) is not None):
> return result
>
> which turns to be tad slow, or at least slower then, say, a direct
> attribute access.
>
> I've noticed that in Python 2.7 TestCase instances now have a
> _resultForDoCleanups attribute that we could use. How safe would it be
> to use it? Are there better options coming along? (Robert mentioned a
> plugin system you're working on). In case there's a proper "fix" coming,
> would it be fine to stick to this private attribute access in the
> meantime?

Revision history for this message
Robert Collins (lifeless) wrote :

On Tue, Aug 21, 2012 at 12:16 AM, Free Ekanayaka
<email address hidden> wrote:
> Hi Robert,
>
> below follows a reply from Michael. Would you be fine with using _resultForDoCleanups for now and possibly file a bug as he suggests?

Sure. Though he has muddled thinking here, because there is no runner
involved. This is purely the TestCase API/protocol interactions.

Revision history for this message
Free Ekanayaka (free.ekanayaka) wrote :

Yeah, I didn't get that bit too, I thought I might be missing something but now you confirm he was just a touch confused :)

Revision history for this message
Jonathan Lange (jml) wrote :

So, where do we want to go from here?

Revision history for this message
Free Ekanayaka (free.ekanayaka) wrote :

I propose to merge this branch as upstream has confirmed it's fine to use _resultForDocCleanups. I can file a bug to the Python tracker describing our use case and asking to eventually support an API to get the result object.

Revision history for this message
Robert Collins (lifeless) wrote :

ok

On Sun, Sep 16, 2012 at 4:33 AM, Free Ekanayaka
<email address hidden> wrote:
> I propose to merge this branch as upstream has confirmed it's fine to use _resultForDocCleanups. I can file a bug to the Python tracker describing our use case and asking to eventually support an API to get the result object.
> --
> https://code.launchpad.net/~free.ekanayaka/testresources/use-test-case-result/+merge/119153
> You are subscribed to branch lp:testresources.

Unmerged revisions

61. By Free Ekanayaka

Use _resultForDoCleanups if available

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/testresources/__init__.py'
2--- lib/testresources/__init__.py 2011-05-03 18:38:27 +0000
3+++ lib/testresources/__init__.py 2012-08-10 15:54:29 +0000
4@@ -678,14 +678,14 @@
5 self.setUpResources()
6
7 def setUpResources(self):
8- setUpResources(self, self.resources, _get_result())
9+ setUpResources(self, self.resources, _get_result(self))
10
11 def tearDown(self):
12 self.tearDownResources()
13 super(ResourcedTestCase, self).tearDown()
14
15 def tearDownResources(self):
16- tearDownResources(self, self.resources, _get_result())
17+ tearDownResources(self, self.resources, _get_result(self))
18
19
20 def setUpResources(test, resources, result):
21@@ -711,16 +711,23 @@
22 delattr(test, resource[0])
23
24
25-def _get_result():
26- """Find a TestResult in the stack.
27+def _get_result(test):
28+ """Find a TestResult in the test case instance or in stack.
29
30- unittest hides the result. This forces us to look up the stack.
31- The result is passed to a run() or a __call__ method 4 or more frames
32- up: that method is what calls setUp and tearDown, and they call their
33- parent setUp etc. Its not guaranteed that the parameter to run will
34+ In Python <= 2.7, unittest hides the result. This forces us to lookup the
35+ stack. The result is passed to a run() or a __call__ method 4 or more
36+ frames up: that method is what calls setUp and tearDown, and they call
37+ their parent setUp etc. Its not guaranteed that the parameter to run will
38 be calls result as its not required to be a keyword parameter in
39 TestCase. However, in practice, this works.
40 """
41+ result = getattr(test, "_resultForDoCleanups", None)
42+
43+ if result is not None:
44+ # Python >= 2.7
45+ return result
46+
47+ # Python < 2.7
48 stack = inspect.stack()
49 for frame in stack[2:]:
50 if frame[3] in ('run', '__call__'):

Subscribers

People subscribed via source and target branches