Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generate full stack traces for inlineCallbacks #814

Merged
merged 12 commits into from
Jun 28, 2018

Conversation

tardyp
Copy link
Contributor

@tardyp tardyp commented Jun 12, 2017

based on #813

fixes https://twistedmatrix.com/trac/ticket/9176

first proof of concept for your review

@jafd
Copy link
Contributor

jafd commented Aug 11, 2017

Just in case, without this hunk

 -        return g.throw(self.type, self.value, self.tb)
 +        # note that the actual magic to find the traceback information is done in _findFailure
 +        return g.throw(self.type, self.value, None)

the one failing test suddenly passes.

I hadn't too much time to investigate why it was necessary to pass None in this one particular case, as every other case does pass self.tb wherever a traceback is needed.

I think I'll need to make a test case that would also check that the erroneous behavior on Python 3 is really gone. Do I make it into a new pull request?

@rodrigc
Copy link
Contributor

rodrigc commented Aug 11, 2017

@jafd You can make a pull request against this branch https://github.com/tardyp/twisted/tree/9176-inlinetracebacks , that way, you and @tardyp can collaborate together an move this along faster.

@tardyp tardyp force-pushed the 9176-inlinetracebacks branch 3 times, most recently from e5a0673 to ce4861a Compare August 14, 2017 16:08
@rodrigc
Copy link
Contributor

rodrigc commented Aug 15, 2017

@tardyp can this PR be closed now that #813 has been updated?

@jafd
Copy link
Contributor

jafd commented Aug 15, 2017

Not quite sure, as this branch adds a specific test for the problem described. Would be a shame to lose it.

@tardyp
Copy link
Contributor Author

tardyp commented Aug 15, 2017

Right. This branch has one commit, which improve the traceback generation for inlineCallbacks. I'll rebase them when #813 lands

@rodrigc rodrigc force-pushed the 9176-inlinetracebacks branch 2 times, most recently from 8e2270e to 1a1dc55 Compare August 17, 2017 17:28
@rodrigc
Copy link
Contributor

rodrigc commented Aug 17, 2017

@tardyp I updated your branch

@rodrigc
Copy link
Contributor

rodrigc commented Aug 17, 2017

@oberstet @itamarst @glyph @exarkun @moshez Please code review this if you have time.


def test_forwardLotsOfTracebacks(self):
"""
Test that several chained inlineCallbacks gives information about all generators
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Docstring here is OK, but try to reword it so that Test that is not there, since it is obvious that it is a test. Twisted prefers to follow
docstring conventions here: https://jml.io/pages/test-docstrings.html

@tardyp
Copy link
Contributor Author

tardyp commented Sep 14, 2017

@rodrigc sorry it took me so long to do this trivial change..

@glyph
Copy link
Member

glyph commented Sep 24, 2017

@tardyp Everybody's got a life to live. You don't need to apologize. Thanks for following up!

@codecov
Copy link

codecov bot commented Sep 29, 2017

Codecov Report

Merging #814 into trunk will decrease coverage by 0.31%.
The diff coverage is 100%.

@@            Coverage Diff             @@
##            trunk     #814      +/-   ##
==========================================
- Coverage   91.78%   91.47%   -0.32%     
==========================================
  Files         843      840       -3     
  Lines      149435   148622     -813     
  Branches    13083    12980     -103     
==========================================
- Hits       137164   135953    -1211     
- Misses      10177    10535     +358     
- Partials     2094     2134      +40

"""
Several Chained inlineCallbacks gives information about all generators

Reproducing #9176
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the docstring for a test should not make a reference to any bugfix, but rather be self-contained and fully describe the performed action and the expected result.

Please use full sentences (with a full stop at the end) and with proper cases.

The full stop is of great help to hint whether the information is complete, or if the sentence was accidentally left unfinished.

@inlineCallbacks
def erroring():
raise Exception()
yield "forcing generator"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

they way this is implemented now, leaves this line without coverage.

Can you do something like

yield "forcing generator"
raise Exception('some-error-marker')

In this way, the function should be converted into a generated and all the code is executed... and the marker text can be used in the assertion to make sure that the right error is raised, and this is not triggered by another error

@adiroiban
Copy link
Member

@tardyp This looks pretty good.

Please look at fixing the coverage and the following small errors

src/twisted/internet/test/test_inlinecb.py (93.9%):
    149: W9012: (Class-level functions should be separated with 2 blank lines.), : Expected 2 blank lines, found 1
    153: W9401: (Used for checking comment format issues.), : Comments should begin with one whitespace

Thanks for your work!

@tardyp
Copy link
Contributor Author

tardyp commented Oct 2, 2017

@adiroiban addressed comments, cleaned-up history and rebased

Copy link
Member

@adiroiban adiroiban left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes look good. Thanks!

See inline comments. I don't understand why we need 2 tests.

If we really need 2, than the docstrings should be more specific about what are the diferences between the 2 tests.

I will create a copy of this branch inside twisted so that we can trigger all the tests.

Meanwhile please check the comments.

Thanks!

Chained inlineCallbacks are forwarding the traceback informations
from generator to generator.

A first simple test with a couple of inline callbacks.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know if this phrase add any useful info.

I think that the docstring should be

Chained inlineCallbacks are forwarding the traceback information from generator to generator and the whole chain is available as part of traceback.

self.assertIn("in erroring", f.getTraceback())
tb = f.getTraceback()
self.assertIn("in erroring", tb)
self.assertIn("Error Marker", tb)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't we also check that "calling" is part of traceback?

"""
Several Chained inlineCallbacks gives information about all generators.

A wider test with a 4 chained inline callbacks.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is the difference between 2 chained callbacks and 4 chained?

Do we need both tests?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree we don't really need two tests, but the current code passes test_forwardTracebacks, but not test_forwardLotsOfTracebacks.

So if there is a regression, this adds the info whether this is in the basic traceback generation, and/or the "advanced" stack traversal code.

Then I like to have a simple basic test, and then more tests which catch corner cases that we may find later (hence the ref to bugs). This is a methodology that we use at Buildbot, I'm perfectly okay to follow the one of Twisted, and to remove the first test, if you insist.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As you are explaining it now, we do need 2 tests:)

Please add all this information in the docstring, so that in 2 years from now, when someone will look at these tests will know why there are 2 tests without checking the whole PR and the history of those changes.

Thanks!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right. I tried to add more details in docstrings, and in failure.py's comments

@@ -252,6 +262,7 @@ def __init__(self, exc_value=None, exc_type=None, exc_tb=None,
tb.tb_lineno, (), ()
))
tb = tb.tb_next
# merging current stack with stack stored in the Failure.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know if this comment is really needed... same for the above comments.

I prefer to see as little comments as possible. For me, the current code is good enough.

In 6 month we might do a change to the code and forget to update the comment ... and then the comment will just confuse rather than help.

We have the docstring for tests to explain all the use cases which this code should support

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added those comments because I did actually write the code 6 month ago, and I couldn't make sense of it at first glance, I needed a good 5 min in order to understand again why I did it like that.
Merging current stack with failure contained stack is the core idea of this trick. This is why I insist on it.
I think future change for this code needs to carefully understand it.
Please let me know which line exactly you think is superfluous.

Test that chained inlineCallbacks are properly forwarding the traceback informations
from generator to generator
Reproducing #9175
Chained inlineCallbacks are forwarding the traceback informations
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have pushed a commit for fixing the informations typo, but I see that it was lost.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sorry. I fixed that.

@adiroiban
Copy link
Member

I added those comments because I did actually write the code 6 month ago, and I couldn't make sense of it at first glance,

If you think that they are needed, that is fine and you can keep them.


For me is more important to have the tests and good docstring to the tests.
If someone don't understand what the merge part is doing and remove it, the test should fail... and then looking at the docstring of the test I hope that you will understand what is going on there :)


But this is not a hard requirement, so if you like the comments I am ok with keeping them.


Once tests are green I will merge them... but I think some will fail due to comment which don't start with capital letter :)

Thanks!

@tardyp
Copy link
Contributor Author

tardyp commented Oct 2, 2017

For me is more important to have the tests and good docstring to the tests.
If someone don't understand what the merge part is doing and remove it, the test should fail... and then looking at the docstring of the test I hope that you will understand what is going on there :)

My reasoning was that the test docstring should explain the code external behaviour, and not the implementation details.

Once tests are green I will merge them... but I think some will fail due to comment which don't start with capital letter :)

Indeed. I fixed those.

adiroiban
adiroiban previously approved these changes Oct 2, 2017
Copy link
Member

@adiroiban adiroiban left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All good. Thanks.

It this a PR for new feature or for fixing a defect? :)

@tardyp
Copy link
Contributor Author

tardyp commented Oct 2, 2017

@adiroiban As a user of Twisted, I would have expected the tracebacks from inlineCallbacks to behave like that. This is why for me it is a very old bug.

One could argue this was never supposed to work and say it is a new feature.

@adiroiban
Copy link
Member

@tardyp > As a user of Twisted, I would have expected the tracebacks from inlineCallbacks to behave like that.

I feel the same.. so maybe this should be a bugfix.


Release notes should contain the full name of the affected python object/class/module.

I have pushed a small commit, also to re-trigger appveyor, as manual trigger failed.


Now, at a second review something is strange with this PR.

The changes are in twisted.python.failure.Failure but the tests are for twisted.internet.defer.inlineCallbacks

In order to pinpoint the error, shouldn't this have at least one test in twisted.test.test_failure?

Thanks!

tardyp and others added 6 commits October 3, 2017 10:59
Before:

twisted.internet.test.test_inlinecb
  ForwardTraceBackTests
    test_forwardLotsOfTracebacks ... Traceback (most recent call last):
  File "twisted/src/twisted/internet/defer.py", line 1532, in unwindGenerator
    return _inlineCallbacks(None, gen, Deferred())
  File "twisted/src/twisted/internet/defer.py", line 1386, in _inlineCallbacks
    result = g.send(result)
  File "twisted/src/twisted/internet/test/test_inlinecb.py", line 162, in calling3
    yield erroring()
  File "twisted/src/twisted/internet/defer.py", line 1532, in unwindGenerator
    return _inlineCallbacks(None, gen, Deferred())
--- <exception caught here> ---
  File "twisted/src/twisted/internet/defer.py", line 1386, in _inlineCallbacks
    result = g.send(result)
  File "twisted/src/twisted/internet/test/test_inlinecb.py", line 157, in erroring
    raise Exception()
builtins.Exception:

After:

twisted.internet.test.test_inlinecb
  ForwardTraceBackTests
    test_forwardLotsOfTracebacks ... Traceback (most recent call last):
  File "twisted/src/twisted/internet/defer.py", line 1532, in unwindGenerator
    return _inlineCallbacks(None, gen, Deferred())
  File "twisted/src/twisted/internet/defer.py", line 1386, in _inlineCallbacks
    result = g.send(result)
  File "twisted/src/twisted/internet/test/test_inlinecb.py", line 162, in calling3
    yield erroring()
  File "twisted/src/twisted/internet/defer.py", line 1532, in unwindGenerator
    return _inlineCallbacks(None, gen, Deferred())
--- <exception caught here> ---
  File "twisted/src/twisted/internet/test/test_inlinecb.py", line 170, in calling
    yield calling2()
  File "twisted/src/twisted/internet/test/test_inlinecb.py", line 166, in calling2
    yield calling3()
  File "twisted/src/twisted/internet/test/test_inlinecb.py", line 162, in calling3
    yield erroring()
  File "twisted/src/twisted/internet/defer.py", line 1386, in _inlineCallbacks
    result = g.send(result)
  File "twisted/src/twisted/internet/test/test_inlinecb.py", line 157, in erroring
    raise Exception()
builtins.Exception:
Actually, that unit test demonstrated an issue with the fact that original
failure was changed by the failure chaining code.
@tardyp
Copy link
Contributor Author

tardyp commented Oct 3, 2017

The changes are in twisted.python.failure.Failure but the tests are for twisted.internet.defer.inlineCallbacks
In order to pinpoint the error, shouldn't this have at least one test in twisted.test.test_failure?

Good point. Actually digging in those test demonstrated an issue with my code... see
cc1ad07

Fixing it adds a bit of inefficiency, though (one dict copy), but it is probably negligible compared to the stack trace fixing code.

@adiroiban adiroiban dismissed their stale review October 3, 2017 10:54

new changes not reviewed

"The new Failure should be chained to that original Failure."

Should it be the exact same Failure?

or should it also include were the Failure was rethrown?
@tardyp
Copy link
Contributor Author

tardyp commented Oct 3, 2017

Added some more unit test fixes. I need to change a test written in 2007 at 4fa3523

The problem is I changed the API... before the rethrown exception was exactly the same as the original exception, now it contains info about the context of where it was re-thrown.

This test has been specifically designed for inline callback. I don't see another use case of throwing a failure as an exception, so I think this make sense to change that API.

@glyph git blame shows peaker as the original writter of this code I am not sure who that is, and if we can jump him in the discussion.

@hawkowl
Copy link
Member

hawkowl commented Mar 31, 2018

Pushed up to buildbot to run the full test suite.

@tardyp
Copy link
Contributor Author

tardyp commented Mar 31, 2018

Hi @hawkowl I fixed txchecker issue. Shall I clean the branch and rebase?

@glyph
Copy link
Member

glyph commented Apr 7, 2018

Rebase or merge trunk, either one is OK!

Copy link
Member

@glyph glyph left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have reviewed this, resolved some trivial conflicts, and it looks good to me. I think that we may want to internally do some refactoring at some point - it seems to me that this really ought to be expressed with something more akin to __cause__ and __context__, rather than treating Failure itself as an exception.

Nevertheless this is clearly more correct than what we have now.

I am going to open another PR just to get a full CI run on the merged result before landing.

@glyph glyph mentioned this pull request Jun 28, 2018
@glyph glyph merged commit 18c72be into twisted:trunk Jun 28, 2018
@tardyp tardyp deleted the 9176-inlinetracebacks branch June 28, 2018 09:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants