Assertion rewriting implementation. The main entrypoint is to call .run() with an ast.Module instance, this will then find all the assert statements and rewrite them to provide intermediate values and a detailed assertion error. See http://pybites.blogspot.be/2011/07/behind-scenes-
| 604 | |
| 605 | |
| 606 | class AssertionRewriter(ast.NodeVisitor): |
| 607 | """Assertion rewriting implementation. |
| 608 | |
| 609 | The main entrypoint is to call .run() with an ast.Module instance, |
| 610 | this will then find all the assert statements and rewrite them to |
| 611 | provide intermediate values and a detailed assertion error. See |
| 612 | http://pybites.blogspot.be/2011/07/behind-scenes-of-pytests-new-assertion.html |
| 613 | for an overview of how this works. |
| 614 | |
| 615 | The entry point here is .run() which will iterate over all the |
| 616 | statements in an ast.Module and for each ast.Assert statement it |
| 617 | finds call .visit() with it. Then .visit_Assert() takes over and |
| 618 | is responsible for creating new ast statements to replace the |
| 619 | original assert statement: it rewrites the test of an assertion |
| 620 | to provide intermediate values and replace it with an if statement |
| 621 | which raises an assertion error with a detailed explanation in |
| 622 | case the expression is false and calls pytest_assertion_pass hook |
| 623 | if expression is true. |
| 624 | |
| 625 | For this .visit_Assert() uses the visitor pattern to visit all the |
| 626 | AST nodes of the ast.Assert.test field, each visit call returning |
| 627 | an AST node and the corresponding explanation string. During this |
| 628 | state is kept in several instance attributes: |
| 629 | |
| 630 | :statements: All the AST statements which will replace the assert |
| 631 | statement. |
| 632 | |
| 633 | :variables: This is populated by .variable() with each variable |
| 634 | used by the statements so that they can all be set to None at |
| 635 | the end of the statements. |
| 636 | |
| 637 | :variable_counter: Counter to create new unique variables needed |
| 638 | by statements. Variables are created using .variable() and |
| 639 | have the form of "@py_assert0". |
| 640 | |
| 641 | :expl_stmts: The AST statements which will be executed to get |
| 642 | data from the assertion. This is the code which will construct |
| 643 | the detailed assertion message that is used in the AssertionError |
| 644 | or for the pytest_assertion_pass hook. |
| 645 | |
| 646 | :explanation_specifiers: A dict filled by .explanation_param() |
| 647 | with %-formatting placeholders and their corresponding |
| 648 | expressions to use in the building of an assertion message. |
| 649 | This is used by .pop_format_context() to build a message. |
| 650 | |
| 651 | :stack: A stack of the explanation_specifiers dicts maintained by |
| 652 | .push_format_context() and .pop_format_context() which allows |
| 653 | to build another %-formatted string while already building one. |
| 654 | |
| 655 | :scope: A tuple containing the current scope used for variables_overwrite. |
| 656 | |
| 657 | :variables_overwrite: A dict filled with references to variables |
| 658 | that change value within an assert. This happens when a variable is |
| 659 | reassigned with the walrus operator |
| 660 | |
| 661 | This state, except the variables_overwrite, is reset on every new assert |
| 662 | statement visited and used by the other visitors. |
| 663 | """ |