Enabling viewing of rewritten AST trees as Python
Hi, In the process of fixing a recent bug <https://bitbucket.org/hpk42/pytest/issue/615/valueerror-on-compound-assert-w...> in pytest source rewriting I found a library to take the rewritten AST tree and generate Python source code from it. Floris mentioned this could be helpful to pytest developers working on the assertion rewriting code. The library is called Meta <https://pypi.python.org/pypi/meta>. (Note, currently only the develop branch on github works successfully on pytest's rewrite tests.) It allows viewing an AST tree as Python source with a single call: import meta log.debug(meta.asttools.dump_python_source(ast_tree)) The question I have: how can pytest developers (and those debugging pytest's rewriting of their tests) best get access to the rewritten AST tree, to be able to view it's Python source representation? The AST tree is seen in pytest/_pytest/assertion/rewrite.py::rewrite_asserts <https://bitbucket.org/hpk42/pytest/src/default/_pytest/assertion/rewrite.py?...> in the mod variable: def rewrite_asserts(mod): """Rewrite the assert statements in mod.""" AssertionRewriter().run(mod) The four options I can think of to gain access to this variable: - monkey patch rewrite.rewrite_asserts - edit rewrite_asserts and paste in the logging code above: -- temporarily, by each developer, on each occasion required -- permanently, but only running when a certain debug flag is active - expose the rewritten AST via a new hook monkey patch is my preferred option, because if I can make it work, a plugin could be written. I just need some help to find an appropriate hook to do the monkey patching before pytest gets going with the AssertionRewritingHook and calls rewrite_asserts. Without knowledge of an early-running hook, I could only get this working by patching rewrite_asserts then calling pytest.main myself, see https://gist.github.com/tomviner/13c95cdb1e159028fc0b Second option, I could explain how to do the temporary edit in a short blog post. This would get past the pytest.main downside to the gist above. But telling people to edit random pytest source files seems bad form. Third option, a pull request on pytest logging the Python source, only if a debug flag is active and Meta is importable. But is this reasonable just for debugging purposes? Alternatively, fourth, a new hook could be added to expose the rewritten AST tree, and everything else could then be a plugin. Thoughts? To see the assertion rewriting pytest does, as Python code, follow the instructions in my gist: https://gist.github.com/tomviner/13c95cdb1e159028fc0b Cheers, Tom
Hi Tom, On 9 November 2014 19:18, Tom Viner <tom@viner.tv> wrote:
The four options I can think of to gain access to this variable: - monkey patch rewrite.rewrite_asserts - edit rewrite_asserts and paste in the logging code above: -- temporarily, by each developer, on each occasion required -- permanently, but only running when a certain debug flag is active - expose the rewritten AST via a new hook
monkey patch is my preferred option, because if I can make it work, a plugin could be written. I just need some help to find an appropriate hook to do the monkey patching before pytest gets going with the AssertionRewritingHook and calls rewrite_asserts. Without knowledge of an early-running hook, I could only get this working by patching rewrite_asserts then calling pytest.main myself, see https://gist.github.com/tomviner/13c95cdb1e159028fc0b
Would this work by using monkeypatching, but using a method on _pytest.pytester.TmpTestDir which wraps around inline_run or inline_runsource? That would seem like a fairly reasonable approach. It could simply skip with an appropriate message if the meta lib is importable and print the reversed python code to stdout for capsys to collect. If you would like to go the full plugin way I believe you just need to hook in before collection happens. I imagine pytest_configure would be a reasonable hook for that, but have to admit I haven't tried it. Regards, Floris
Thanks for your ideas Floris, I've managed to get it working with your pytest_configure suggestion. The only extra bit was having to disable reading the cached pyc files that pytest writes, as these skip my monkeypatch. Take a look, it's simple to use: https://gist.github.com/tomviner/c3537c2f2b2b8172f83e If there's any demand, I could easily make this a pip installable pytest plugin. On 13 November 2014 at 23:34, Floris Bruynooghe <flub@devork.be> wrote:
Hi Tom,
The four options I can think of to gain access to this variable: - monkey patch rewrite.rewrite_asserts - edit rewrite_asserts and paste in the logging code above: -- temporarily, by each developer, on each occasion required -- permanently, but only running when a certain debug flag is active - expose the rewritten AST via a new hook
monkey patch is my preferred option, because if I can make it work, a
On 9 November 2014 19:18, Tom Viner <tom@viner.tv> wrote: plugin
could be written. I just need some help to find an appropriate hook to do the monkey patching before pytest gets going with the AssertionRewritingHook and calls rewrite_asserts. Without knowledge of an early-running hook, I could only get this working by patching rewrite_asserts then calling pytest.main myself, see https://gist.github.com/tomviner/13c95cdb1e159028fc0b
Would this work by using monkeypatching, but using a method on _pytest.pytester.TmpTestDir which wraps around inline_run or inline_runsource? That would seem like a fairly reasonable approach. It could simply skip with an appropriate message if the meta lib is importable and print the reversed python code to stdout for capsys to collect.
If you would like to go the full plugin way I believe you just need to hook in before collection happens. I imagine pytest_configure would be a reasonable hook for that, but have to admit I haven't tried it.
Regards, Floris
One extra note: `py.test -s` is required to see rewritten AST output, even for failing tests, because the output comes while tests are being collected and rewritten, not during the execution of a test. So pytest's "show output upon fail" cannot help here. If anyone knows how to tie the output to particular tests, that would be great. On 1 March 2015 at 23:01, Tom Viner <tom@viner.tv> wrote:
Thanks for your ideas Floris, I've managed to get it working with your pytest_configure suggestion.
The only extra bit was having to disable reading the cached pyc files that pytest writes, as these skip my monkeypatch.
Take a look, it's simple to use: https://gist.github.com/tomviner/c3537c2f2b2b8172f83e
If there's any demand, I could easily make this a pip installable pytest plugin.
On 13 November 2014 at 23:34, Floris Bruynooghe <flub@devork.be> wrote:
Hi Tom,
The four options I can think of to gain access to this variable: - monkey patch rewrite.rewrite_asserts - edit rewrite_asserts and paste in the logging code above: -- temporarily, by each developer, on each occasion required -- permanently, but only running when a certain debug flag is active - expose the rewritten AST via a new hook
monkey patch is my preferred option, because if I can make it work, a
On 9 November 2014 19:18, Tom Viner <tom@viner.tv> wrote: plugin
could be written. I just need some help to find an appropriate hook to do the monkey patching before pytest gets going with the AssertionRewritingHook and calls rewrite_asserts. Without knowledge of an early-running hook, I could only get this working by patching rewrite_asserts then calling pytest.main myself, see https://gist.github.com/tomviner/13c95cdb1e159028fc0b
Would this work by using monkeypatching, but using a method on _pytest.pytester.TmpTestDir which wraps around inline_run or inline_runsource? That would seem like a fairly reasonable approach. It could simply skip with an appropriate message if the meta lib is importable and print the reversed python code to stdout for capsys to collect.
If you would like to go the full plugin way I believe you just need to hook in before collection happens. I imagine pytest_configure would be a reasonable hook for that, but have to admit I haven't tried it.
Regards, Floris
participants (2)
-
Floris Bruynooghe -
Tom Viner